yesod-recaptcha 0.1 → 0.1.1
raw patch · 2 files changed
+81/−43 lines, 2 files
Files
- src/Yesod/ReCAPTCHA.hs +80/−42
- yesod-recaptcha.cabal +1/−1
src/Yesod/ReCAPTCHA.hs view
@@ -48,9 +48,43 @@ -- The 'YA.YesodAuth' superclass is used only for the HTTP -- request. Please fill a bug report if you think that this -- @YesodReCAPTCHA@ may be useful without @YesodAuth@.+--+-- /Minimum complete definition:/ 'recaptchaPublicKey' and+-- 'recaptchaPrivateKey'. class YA.YesodAuth master => YesodReCAPTCHA master where+ -- | Your reCAPTCHA public key. recaptchaPublicKey :: YC.GHandler sub master T.Text+ -- | Your reCAPTCHA private key. recaptchaPrivateKey :: YC.GHandler sub master T.Text+ -- | A backdoor to the reCAPTCHA mechanism. While doing+ -- automated tests you may need to fill a form that is+ -- protected by a CAPTCHA. The whole point of using a+ -- CAPTCHA is disallowing access to non-humans, which+ -- hopefully your test suite is.+ --+ -- In order to solve this problem, you may define+ --+ -- > insecureRecaptchaBackdoor = return (Just "<secret CAPTCHA>")+ --+ -- Now, whenever someone fills @\<secret CAPTCHA\>@ as the+ -- CAPTCHA, the @yesod-recaptcha@ library will /not/ contact+ -- reCAPTCHA's servers and instead will blindly accept the+ -- secret CAPTCHA.+ --+ -- Note that this is a *huge* security hole in the wrong+ -- hands. We /do not/ recommend using this function on a+ -- production environment without a good reason. If for+ -- whatever reason you must use this function on a production+ -- environment, please make use of its access to 'GHandler'+ -- in order to return @Just@ only when strictly necessary.+ -- For example, you may return @Just@ only when the request+ -- comes from @localhost@ and read its contents from a secret+ -- file accessible only by SSH which is afterwards removed.+ --+ -- By default, this function returns @Nothing@, which+ -- completely disables the backdoor.+ insecureRecaptchaBackdoor :: YC.GHandler sub master (Maybe T.Text)+ insecureRecaptchaBackdoor = return Nothing -- | A reCAPTCHA field. This 'YF.AForm' returns @()@ because@@ -124,48 +158,52 @@ check "" _ = return $ Error "invalid-request-cookie" check _ "" = return $ Error "incorrect-captcha-sol" check challenge response = do- manager <- YA.authHttpManager <$> YC.getYesod- privateKey <- recaptchaPrivateKey- sockaddr <- W.remoteHost <$> YC.waiRequest- case sockaddr of- HS.SockAddrUnix _ -> do- $(YC.logError) $ "Yesod.ReCAPTCHA: Couldn't find out remote IP, \- \are you using a reverse proxy? If yes, then \- \please file a bug report at \- \<https://github.com/meteficha/yesod-recaptcha>."- fail "Could not find remote IP address for reCAPTCHA."- _ -> do- let remoteip = case sockaddr of- HS.SockAddrInet _ hostAddr ->- show $ NI.IPv4 hostAddr- HS.SockAddrInet6 _ _ (w1, w2, w3, w4) _ ->- show $ NI.IPv6 w1 w2 w3 w4- HS.SockAddrUnix _ -> error "ReCAPTCHA.check"- req = H.def- { H.method = HT.methodPost- , H.host = "www.google.com"- , H.path = "/recaptcha/api/verify"- , H.queryString = HT.renderSimpleQuery False query- }- query = [ ("privatekey", TE.encodeUtf8 privateKey)- , ("remoteip", B8.pack remoteip)- , ("challenge", TE.encodeUtf8 challenge)- , ("response", TE.encodeUtf8 response)- ]- eresp <- E.try $ C.runResourceT $ H.httpLbs req manager- case (L8.lines . H.responseBody) <$> eresp of- Right ("true":_) -> return Ok- Right ("false":why:_) -> return . Error . TL.toStrict $- TLE.decodeUtf8With TEE.lenientDecode why- Right other -> do- $(YC.logError) $ T.concat [ "Yesod.ReCAPTCHA: could not parse "- , T.pack (show other) ]- return (Error "recaptcha-not-reachable")- Left exc -> do- $(YC.logError) $ T.concat [ "Yesod.ReCAPTCHA: could not contact server ("- , T.pack (show (exc :: E.SomeException))- , ")" ]- return (Error "recaptcha-not-reachable")+ backdoor <- insecureRecaptchaBackdoor+ if Just response == backdoor+ then return Ok+ else do+ manager <- YA.authHttpManager <$> YC.getYesod+ privateKey <- recaptchaPrivateKey+ sockaddr <- W.remoteHost <$> YC.waiRequest+ case sockaddr of+ HS.SockAddrUnix _ -> do+ $(YC.logError) $ "Yesod.ReCAPTCHA: Couldn't find out remote IP, \+ \are you using a reverse proxy? If yes, then \+ \please file a bug report at \+ \<https://github.com/meteficha/yesod-recaptcha>."+ fail "Could not find remote IP address for reCAPTCHA."+ _ -> do+ let remoteip = case sockaddr of+ HS.SockAddrInet _ hostAddr ->+ show $ NI.IPv4 hostAddr+ HS.SockAddrInet6 _ _ (w1, w2, w3, w4) _ ->+ show $ NI.IPv6 w1 w2 w3 w4+ HS.SockAddrUnix _ -> error "ReCAPTCHA.check"+ req = H.def+ { H.method = HT.methodPost+ , H.host = "www.google.com"+ , H.path = "/recaptcha/api/verify"+ , H.queryString = HT.renderSimpleQuery False query+ }+ query = [ ("privatekey", TE.encodeUtf8 privateKey)+ , ("remoteip", B8.pack remoteip)+ , ("challenge", TE.encodeUtf8 challenge)+ , ("response", TE.encodeUtf8 response)+ ]+ eresp <- E.try $ C.runResourceT $ H.httpLbs req manager+ case (L8.lines . H.responseBody) <$> eresp of+ Right ("true":_) -> return Ok+ Right ("false":why:_) -> return . Error . TL.toStrict $+ TLE.decodeUtf8With TEE.lenientDecode why+ Right other -> do+ $(YC.logError) $ T.concat [ "Yesod.ReCAPTCHA: could not parse "+ , T.pack (show other) ]+ return (Error "recaptcha-not-reachable")+ Left exc -> do+ $(YC.logError) $ T.concat [ "Yesod.ReCAPTCHA: could not contact server ("+ , T.pack (show (exc :: E.SomeException))+ , ")" ]+ return (Error "recaptcha-not-reachable") -- | See 'check'.
yesod-recaptcha.cabal view
@@ -1,5 +1,5 @@ Name: yesod-recaptcha-Version: 0.1+Version: 0.1.1 Synopsis: Dead simple support for reCAPTCHA on Yesod applications. Homepage: https://github.com/meteficha/yesod-recaptcha License: BSD3