Page 1 of 1

Create your own Captcha for Security on Login Page

Posted: Fri Dec 19, 2025 1:58 pm
by admin
Providing Captcha on your Oracle APEX login page is a security requirement which any public application must have to handle automated logins.

Captcha1.png
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.
  1. Create a new application in Oracle APEX or open exisiting login page P9999.
  2. Create following

    a. Static Content Region
    b. :P9999_CAPTCHA
    c. :P9999_SECRET_CAPTCHA << It will be hidden and Value Protected off.
    Captcha2.png
  3. 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();
        });
      }
    });
    
  4. 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(); 
    });
    
    captcha3.png
  5. 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;
    captcha4.png
  6. 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.
    Captch5.png