본문 바로가기

보안/시큐어코딩

[시큐어코딩실습] CSRF 방어를 위한 CAPCHA 사용방법

작성자: 전은수팀장/오픈이지 보안기술팀

 

1. jcaptcha-1.0-all.jar와 commons-collections-3.2.1.jar를 lib에 넣는다
D:\javaDev\workspace\openeg\WebContent\WEB-INF\lib

libs.zip

 

 

2.write.jsp에 다음 내용을 추가한다

 

write.jsp.txt

<input type="hidden" name="hidCaptchaID" value="<%= session.getId() %>"/>  

   Enter these letters: &nbsp; &nbsp; <img  class="captcha"  src="getCaptcha.do"  
   align="middle" alt="Enter the characters appearing in this image" border="10 "/>
<input type="text" name="inCaptchaChars"/>

 

3. MyCaptchaService.java를 작성한다

 

MyCaptchaService.java.txt

package kr.co.openeg.lab.test;

import com.octo.captcha.service.image.ImageCaptchaService;
import com.octo.captcha.service.image.DefaultManageableImageCaptchaService;
public class MyCaptchaService
{
  // a singleton class
  private static ImageCaptchaService instance = new DefaultManageableImageCaptchaService();
  public static ImageCaptchaService getInstance()
  {
    return instance;
  }
}  



4. BoardController.java에 다음 내용을 추가한다.

 

BoardController.java.txt

 

/////////////////////////////////////////////////

@RequestMapping(value = "/getCaptcha.do")
 protected void getCaptcha(HttpServletRequest request, HttpServletResponse response)
                                       throws ServletException, IOException {

  String sImgType = "png";
  ByteArrayOutputStream imgOutputStream = new ByteArrayOutputStream();
  byte[] captchaBytes;

  if (request.getQueryString() != null) {
   response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                               "GET request should have no query string.");

   return;
  }
  try {
   // Session ID is used to identify the particular captcha.
   String captchaId = request.getSession().getId();

   // Generate the captcha image.
   BufferedImage challengeImage = MyCaptchaService.getInstance()
                                                  .getImageChallengeForID(captchaId,
request.getLocale());

   ImageIO.write(challengeImage, sImgType, imgOutputStream);
   captchaBytes = imgOutputStream.toByteArray();

  

   // Clear any existing flag.
   request.getSession().removeAttribute("PassedCaptcha");
  } catch (CaptchaServiceException cse) {
   System.out.println("CaptchaServiceException - " + cse.getMessage());
   response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                               "Problem generating captcha image.");

   return;
  } catch (IOException ioe) {
   System.out.println("IOException - " + ioe.getMessage());
   response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                               "Problem generating captcha image.");

   return;
  }

  // Set appropriate http headers.
  response.setHeader("Cache-Control", "no-store");
  response.setHeader("Pragma", "no-cache");
  response.setDateHeader("Expires", 0);
  response.setContentType("image/" + (sImgType.equalsIgnoreCase("png") ? "png" : "jpeg"));

  // Write the image to the client.
  ServletOutputStream outStream = response.getOutputStream();
  outStream.write(captchaBytes);
  outStream.flush();
  outStream.close();
 }


///////////////////////////////////////


protected boolean processCaptcha( HttpServletRequest request) 
    
     {
       // Get the request params.
       Map paramMap = request.getParameterMap();
       if ( paramMap.isEmpty() )
       {
        return false; 
       }
       String[] arr1 = (String[])paramMap.get( "hidCaptchaID" );
       String[] arr2 = (String[])paramMap.get( "inCaptchaChars" );
       System.out.println("========>"+arr1[0]+":"+arr2[0]);
       if ( arr1==null || arr2==null  )
       {
        return false; 
       }

       String sessId = request.getSession().getId();
       String incomingCaptchaId = arr1.length>0 ? arr1[0] : "";
       String inputChars = arr2.length>0 ? arr2[0] : "";

       // Check validity and consistency of the data.
       if ( sessId==null || incomingCaptchaId==null || !sessId.equals(incomingCaptchaId) )
       {
        return false; 
       }

       // Validate whether input from user is correct.
       System.out.println( "Validating - inputChars are: " + inputChars );
       boolean passedCaptchaTest = validateCaptcha( incomingCaptchaId, inputChars );
       System.out.println(passedCaptchaTest);
       // Set flag into session.
       request.getSession().setAttribute( "PassedCaptcha", new Boolean(passedCaptchaTest) );

       return passedCaptchaTest;
     }


    ///////////////////////////////////////////////


    private boolean validateCaptcha( String captchaId, String inputChars )
     {
       boolean bValidated = false;
       try
       {
         bValidated = MyCaptchaService.getInstance().validateResponseForID( captchaId, inputChars );
       }
       catch( CaptchaServiceException cse )
       {cse.printStackTrace();}
       return bValidated;
     }


   //////////////////////////////////////////////////

 
  @RequestMapping(value="/write.do", method=RequestMethod.POST)
  public String boardWriteProc(@ModelAttribute("BoardModel") BoardModel boardModel,                      
                                           MultipartHttpServletRequest request, HttpSession session){  

  if(!processCaptcha(request)){
      System.out.println("captcha test fail!");
      session.setAttribute("writeErrorCode", 3); 
      return "redirect:list.do"; 
  }
  
  MultipartFile file = request.getFile("file");    
  ...