Best practices for CAPTCHA
- Generate the code
- Use a random string for the CAPTCHA Mix letters and numbers (avoid ambiguous characters like 0 vs O or 1 vs l).
- Store the CAPTCHA value in a hidden page item
- Redraw on demand (refresh icon) User can request a new CAPTCHA if the current one is hard to read.
- Validate before login processing In a Before Header or Page Process, check if the user-entered value matches the stored CAPTCHA. Reject login if it doesn’t match.
- Use image distortions and noise Makes it harder for automated bots to read the text.
- Create a new application in Oracle APEX or open exisiting login page P9999.
- Create following
a. Static Content Region
b. :P9999_CAPTCHA
c. :P9999_SECRET_CAPTCHA << It will be hidden and Value Protected off.
- Now click page and copy paste following code in Function and Global Variable Declaration
Code: Select all
// Draw CAPTCHA on canvas function drawCaptcha(rawText) { const canvas = document.getElementById("captcha"); const ctx = canvas.getContext("2d"); const colors = ["#e53935","#1e88e5","#43a047","#fb8c00","#8e24aa","#00897b"]; ctx.clearRect(0, 0, canvas.width, canvas.height); // Background const bg = ctx.createLinearGradient(0,0,canvas.width,canvas.height); bg.addColorStop(0,"#ffffff"); bg.addColorStop(1,"#eaeaea"); ctx.fillStyle = bg; ctx.fillRect(0,0,canvas.width,canvas.height); // Random curves for (let i=0;i<8;i++) { ctx.strokeStyle = colors[Math.floor(Math.random()*colors.length)] + "80"; ctx.lineWidth = 3; ctx.beginPath(); ctx.moveTo(0, Math.random()*canvas.height); ctx.bezierCurveTo( canvas.width*0.3, Math.random()*canvas.height, canvas.width*0.6, Math.random()*canvas.height, canvas.width, Math.random()*canvas.height ); ctx.stroke(); } // Draw letters ctx.font = "50px Arial"; ctx.textAlign = "center"; ctx.textBaseline = "middle"; const gap = 42; const totalWidth = (rawText.length - 1) * gap; const centerX = canvas.width/2 - totalWidth/2; for (let i=0;i<rawText.length;i++) { ctx.save(); ctx.translate(centerX + i*gap, canvas.height/2 + (Math.random()*16 - 8)); ctx.rotate((Math.random()*40-20) * Math.PI/180); const grad = ctx.createLinearGradient(-20,-30,20,30); grad.addColorStop(0,"#e53935"); grad.addColorStop(0.5,"#1e88e5"); grad.addColorStop(1,"#43a047"); ctx.fillStyle = grad; ctx.fillText(rawText[i],0,0); ctx.restore(); } // Noise dots for (let i=0;i<250;i++){ ctx.fillStyle = colors[Math.floor(Math.random()*colors.length)] + "99"; ctx.fillRect(Math.random()*canvas.width, Math.random()*canvas.height, 2, 2); } // Noise lines for (let i=0;i<10;i++){ ctx.strokeStyle = colors[Math.floor(Math.random()*colors.length)] + "aa"; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(Math.random()*canvas.width, Math.random()*canvas.height); ctx.lineTo(Math.random()*canvas.width, Math.random()*canvas.height); ctx.stroke(); } } // Generate random CAPTCHA code function generateCaptcha(length=6) { const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789abcdefghijklmnpqrstuvwxyz'; let code = ''; for(let i=0;i<length;i++){ code += chars[Math.floor(Math.random()*chars.length)]; } // Draw and update hidden item drawCaptcha(code); $s("P9999_SECRET_CAPTCHA", code); $s("P9999_CAPTCHA", ""); } // Initial draw on page load document.addEventListener("DOMContentLoaded", function(){ generateCaptcha(); // Attach refresh click handler const refreshIcon = document.getElementById("captchaRefresh"); if(refreshIcon){ refreshIcon.addEventListener("click", function(){ generateCaptcha(); }); } }); - Create a dynamic action and set its eent to Page Load. In true action select Excecute Java Code and copy paste following code.
Code: Select all
document.addEventListener("DOMContentLoaded", function() { generateCaptcha(); }); - Create a validation and enter following code and provide yoru error message to display.
Code: Select all
BEGIN IF :P9999_CAPTCHA IS NOT NULL THEN IF :P9999_CAPTCHA!= :P9999_SECRET_CAPTCHA THEN RETURN FALSE; ELSE RETURN TRUE; END IF; ELSE RETURN FALSE; END IF; END; - Now test by entering wrong captcha and then correct. Here I generated captcha on client side to avoid slite delay while generate using plsql on server side.