์ด ํ์ด์ง์์๋ reCAPTCHA์ ๋น๋ฐ๋ฒํธ ๋ฐฉ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ๋น๋ฐ๋ฒํธ ์ ์ถ ๋ฐ ์ ์ถ๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ๊ฐ์งํ์ฌ ๊ณ์ ํ์ทจ(ATO)์ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ๋ฐ๋ณต ์ ๋ ฅ ๊ณต๊ฒฉ์ ๋ฐฉ์งํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์ค๋ช ํฉ๋๋ค. reCAPTCHA์์๋ ํ๊ฐ์ ์ผ๋ถ๋ก ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด(๋น๋ฐ๋ฒํธ)๋ฅผ ์ ๊ธฐ์ ์ผ๋ก ๊ฐ์ฌํ์ฌ ์ ์ถ ๋๋ ์๋ฐ์ด ์๋์ง ํ์ธํ ์ ์์ต๋๋ค. ์ด๋ฌํ ํ๊ฐ๋ฅผ ์ํํ๊ธฐ ์ํด Google์ ๋น๋ฐ๋ฒํธ ์ง๋จ ๊ธฐ๋ฅ์ ์ฌ์ฉํฉ๋๋ค.
์์ํ๊ธฐ ์ ์
Verify that billing is enabled for your Google Cloud project.
reCAPTCHA์์ ๋น๋ฐ๋ฒํธ ๋ฐฉ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ ค๋ฉด ํ๋ก์ ํธ์ ๊ฒฐ์ ๋ฅผ ์ฐ๊ฒฐํ๊ณ ์ฌ์ฉ ์ค์ ํด์ผ ํฉ๋๋ค. ์ ์ฉ์นด๋ ๋๋ ๊ธฐ์กด Google Cloud ํ๋ก์ ํธ ๊ฒฐ์ ID๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒฐ์ ๋ฅผ ์ฌ์ฉ ์ค์ ํ ์ ์์ต๋๋ค. ๊ฒฐ์ ์ ๊ด๋ จํด ๋์์ด ํ์ํ๋ฉด Cloud Billing ์ง์ํ์ ๋ฌธ์ํ์ธ์.
์๋ฐ ๋ฐ ์ ์ถ๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ํ์ธ
์ํธํ ํจ์๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ Docker ์ปจํ ์ด๋๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ์ธํธ๊ฐ ์์๋์๋์ง ํ์ธํ ์ ์์ต๋๋ค.
Docker ์ปจํ ์ด๋๋ ์ต์ข ์ฌ์ฉ์ ๊ฐ์ธ ์ ๋ณด๋ฅผ ๋ณดํธํ๊ณ ๋น๋ฐ๋ฒํธ ์ ์ถ์ ์์ ํ๊ฒ ์กฐํํ๋ ๋ฐ ํ์ํ ๋ณด์ ๋ค์๊ฐ ์ฐ์ฐ์ ๊ตฌํํ๋ ์คํ์์ค ํด๋ผ์ด์ธํธ์ ๋๋ค. ์์ธํ ๋ด์ฉ์ GitHub ์ ์ฅ์๋ฅผ ์ฐธ์กฐํ์ธ์. Docker ์ปจํ ์ด๋๋ ์ํธํ ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ์ ๋ณต์ก์ฑ์ ์ถ์ํํ๊ณ ์ค์น ํ๋ก์ธ์ค๋ฅผ ๊ฐ์ํํฉ๋๋ค. ๋ํ ์ธํ๋ผ์์ ์ปจํ ์ด๋ ์ฑ์ ํธ์คํ ํ ์ ์๊ฒ ํด์ค๋๋ค.
์ํธํ ํจ์
์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๊ฐ ์ ์ถ๋์๋์ง ํ์ธํ๋ ค๋ฉด ๋ก๊ทธ์ธ, ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ, ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ๊ณผ ๊ฐ์ ์์ ํ๊ฐ ์ ๋น๋ฐ๋ฒํธ ๋ฐฉ์ด๋ฅผ ์ฌ์ฉํฉ๋๋ค.
๋น๋ฐ๋ฒํธ ์ ์ถ ๋ฐ ์ ์ถ๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ํ์ธํ๋ ค๋ฉด ๋ค์ ๋จ๊ณ๋ฅผ ์๋ฃํ์ธ์.
- ์์ฒญ ๋งค๊ฐ๋ณ์๋ฅผ ์์ฑํฉ๋๋ค.
- ๋น๋ฐ๋ฒํธ ์ ์ถ์ ๊ฐ์งํ๋ ํ๊ฐ๋ฅผ ๋ง๋ญ๋๋ค.
- ํ๊ฐ์์ ์ ์ถ๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ํ์ธํฉ๋๋ค.
- ๊ฒฐ๊ณผ๋ฅผ ํด์ํ๊ณ ์กฐ์น๋ฅผ ์ทจํฉ๋๋ค.
์์ฒญ ๋งค๊ฐ๋ณ์ ์์ฑ
๋์ ์์ค์ ๊ฐ์ธ ์ ๋ณด ๋ณดํธ ํ๋กํ ์ฝ์์ ์๊ตฌํ๋ ์ํธํ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํ์ํ ์์ฒญ ๋งค๊ฐ๋ณ์๋ฅผ ๊ณ์ฐํด์ผ ํฉ๋๋ค. reCAPTCHA๋ ์ด๋ฌํ ํ๋๋ฅผ ์์ฑํ๋ ๋ฐ ๋์์ด ๋๋ Java ๋ฐ TypeScript ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋น๋ฐ๋ฒํธ ํ์ธ ์ธ์ฆ์ ๋ง๋ค๋ ค๋ฉด
PasswordCheckVerifier
๊ฐ์ฒด๋ฅผ ๋ง๋ญ๋๋ค.PasswordCheckVerifier verifier = new PasswordCheckVerifier();
์ธ์ฆ์ ์์ํ๋ ค๋ฉด
PasswordCheckVerifier#createVerification
์ ํธ์ถํฉ๋๋ค. ์ด ๋ฉ์๋๋ ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋ฐ๋ฒํธ ํ์ธ์ ์ํํ๋ ๋งค๊ฐ๋ณ์๋ฅผ ๊ณ์ฐํฉ๋๋ค.PasswordCheckVerification verification = verifier.createVerification("username", "password").get();
์ธ์ฆ ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ํ๊ฐ๋ฅผ ๋ง๋ญ๋๋ค.
byte[] lookupHashPrefix = verification.getLookupHashPrefix(); byte[] encryptedUserCredentialsHash = verification.getEncryptedUserCredentialsHash();
๋ฐ์ดํธ ๋ฐฐ์ด
lookupHashPrefix
๋ฐencryptedUserCredentialsHash
์๋ ๋น๋ฐ๋ฒํธ ํ์ธAssessment
๋ฅผ ์์ํ๋ ๋ฐ ํ์ํ ๋งค๊ฐ๋ณ์๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
๋น๋ฐ๋ฒํธ ์ ์ถ ๊ฐ์ง๋ฅผ ์ํ ํ๊ฐ ๋ง๋ค๊ธฐ
projects.assessments.create
๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์์ฒญ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ ์ ๋ค์์ ๋ฐ๊ฟ๋๋ค.
- PROJECT_ID: Google Cloud ํ๋ก์ ํธ ID
- LOOKUP_HASH_PREFIX: ์ฌ์ฉ์ ์ด๋ฆ SHA-256 ํด์ ํ๋ฆฌํฝ์ค์ ํ๋ฆฌํฝ์ค
- ENCRYPTED_USER_CREDENTIALS_HASH: ์ํธํ๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด Scrypt ํด์
HTTP ๋ฉ์๋ ๋ฐ URL:
POST https://recaptchaenterprise.googleapis.com/v1/projects/PROJECT_ID/assessments
JSON ์์ฒญ ๋ณธ๋ฌธ:
{ "private_password_leak_verification": { "lookup_hash_prefix": "LOOKUP_HASH_PREFIX", "encrypted_user_credentials_hash": "ENCRYPTED_USER_CREDENTIALS_HASH" } }
์์ฒญ์ ๋ณด๋ด๋ ค๋ฉด ๋ค์ ์ต์ ์ค ํ๋๋ฅผ ์ ํํฉ๋๋ค.
curl
์์ฒญ ๋ณธ๋ฌธ์ request.json
ํ์ผ์ ์ ์ฅํ๊ณ ๋ค์ ๋ช
๋ น์ด๋ฅผ ์คํํฉ๋๋ค.
curl -X POST \
-H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H "Content-Type: application/json; charset=utf-8" \
-d @request.json \
"https://recaptchaenterprise.googleapis.com/v1/projects/PROJECT_ID/assessments"
PowerShell
์์ฒญ ๋ณธ๋ฌธ์ request.json
ํ์ผ์ ์ ์ฅํ๊ณ ๋ค์ ๋ช
๋ น์ด๋ฅผ ์คํํฉ๋๋ค.
$cred = gcloud auth print-access-token
$headers = @{ "Authorization" = "Bearer $cred" }
Invoke-WebRequest `
-Method POST `
-Headers $headers `
-ContentType: "application/json; charset=utf-8" `
-InFile request.json `
-Uri "https://recaptchaenterprise.googleapis.com/v1/projects/PROJECT_ID/assessments" | Select-Object -Expand Content
๋ค์๊ณผ ๋น์ทํ JSON ์๋ต์ด ํ์๋ฉ๋๋ค.
{ "name": "projects/698047609967/assessments/fb22000000000000", "score": 0, "reasons": [], "privatePasswordLeakVerification": { "lookupHashPrefix": "zoxZwA==", "encryptedUserCredentialsHash": "AyRihRcKaGLj/FA/r2uqQY/fzfTaDb/nEcIUMeD3Tygp", "reencryptedUserCredentialsHash": "Aw65yEbLM39ww1ridDEfx5VhkWo11tzn/R1B88Qqwr/+" "encryptedLeakMatchPrefixes": [ "n/n5fvPD6rmQPFyb4xk=", "IVQqzXsbZenaibID6OI=", ..., "INeMMndrfnlf6osCVvs=", "MkIpxt2x4mtyBnRODu0=", "AqUyAUWzi+v7Kx03e6o="] } }
ํ๊ฐ์์ ์ ์ถ๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ํ์ธ
ํ๊ฐ ์๋ต์์ reEncryptedUserCredentials
๋ฐ encryptedLeakMatchPrefixes
ํ๋๋ฅผ ์ถ์ถํ๊ณ ํ์ธ์ ๊ฐ์ฒด์ ์ ๋ฌํ์ฌ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ์ ์ถ ์ฌ๋ถ๋ฅผ ํ์ธํฉ๋๋ค.
PasswordCheckResult result = verifier.verify(verification,
result.getReEncryptedUserCredentials(),
result.getEncryptedLeakMatchPrefixes()
).get();
System.out.println("Credentials leaked: " + result.areCredentialsLeaked());
์ฝ๋ ์ํ
Node.js(TypeScript)
Node.js (TypeScript)๋ฅผ ์ฌ์ฉํ์ฌ ๋น๋ฐ๋ฒํธ ์ ์ถ ๊ฐ์ง๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด๋ ค๋ฉด GitHub์ TypeScript ์ฝ๋ ์ํ์ ์ฐธ๊ณ ํ์ธ์.
์๋ฐ
reCAPTCHA์ ์ธ์ฆํ๋ ค๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ๊ธฐ๋ณธ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ค์ ํฉ๋๋ค. ์์ธํ ๋ด์ฉ์ ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ธ์ฆ ์ค์ ์ ์ฐธ์กฐํ์ธ์.
Docker ์ปจํ ์ด๋
์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๊ฐ ์ ์ถ๋์๋์ง ํ์ธํ๋ ค๋ฉด localhost ์ฐ๊ฒฐ์ ์ฌ์ฉํ๊ฑฐ๋ ์ปจํ ์ด๋์์ HTTPS๋ฅผ ์ค์ ํ์ฌ ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ์์ ์ปจํ ์ด๋์ ์์ ํ๊ฒ ์ ์กํฉ๋๋ค. ๊ทธ๋ฌ๋ฉด ์ปจํ ์ด๋์์ ์ด๋ฌํ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๋ฅผ ์ํธํํ ํ API ์์ฒญ์ reCAPTCHA์ ๋ณด๋ด๊ณ ๋ก์ปฌ์์ ๋ค์ ์ํธํ๋ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํฉ๋๋ค.
์์ฒญ์ Docker ์ปจํ ์ด๋์ ์ ์กํ๋ ค๋ฉด ๋ค์ ๋จ๊ณ๋ฅผ ์๋ฃํฉ๋๋ค.
- Docker๋ฅผ ์ค์ ํฉ๋๋ค.
- Docker ์ปจํ ์ด๋ ํ๊ฒฝ์ ์ค๋นํฉ๋๋ค.
- ์ปจํ ์ด๋๋ฅผ ๋น๋ํ๊ณ ์คํํฉ๋๋ค.
- HTTP ์์ฒญ์ ์ปจํ ์ด๋์ ์ ์กํฉ๋๋ค.
- ๊ฒฐ๊ณผ๋ฅผ ํด์ํ๊ณ ์กฐ์น๋ฅผ ์ทจํฉ๋๋ค.
Docker ์ปจํ ์ด๋ ์คํ ์ค๋น
์ธ์ฆ ์ ๋ต์ ์ ํํฉ๋๋ค.
์ปจํ ์ด๋๋ ์ ํ๋ฆฌ์ผ์ด์ ๊ธฐ๋ณธ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ์ค์ ์ ์ง์ํ๊ฑฐ๋ ์ธ์ฆ์ ์ํด API ํค๋ฅผ ์๋ฝํ ์ ์์ต๋๋ค.
PLD ์ปจํ ์ด๋๊ฐ HTTPS ๋๋ localhost ์ ์ฉ ๋ฐ๋ชจ ๋ชจ๋์์ ์คํ๋๋๋ก ๊ตฌ์ฑํฉ๋๋ค.
์ปจํ ์ด๋๋ ๋ฏผ๊ฐํ ์ต์ข ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด(์ฌ์ฉ์ ์ด๋ฆ ๋ฐ ๋น๋ฐ๋ฒํธ)๋ฅผ ํ์ฉํ๋ฏ๋ก HTTPS ๋๋ localhost ์ ์ฉ ๋ฐ๋ชจ ๋ชจ๋์์ ์คํ๋์ด์ผ ํฉ๋๋ค. HTTPS ๊ตฌ์ฑ ๋ฐฉ๋ฒ์ ๋ํ ์๋ด๋ GitHub์ README๋ฅผ ์ฐธ์กฐํ์ธ์.
๋ค์ ๋จ๊ณ์์๋ API ํค ์ธ์ฆ์ ์ฌ์ฉํ๊ณ localhost ์ ์ฉ ๋ฐ๋ชจ ๋ชจ๋์์ ํด๋ผ์ด์ธํธ๋ฅผ ์คํํฉ๋๋ค.
Docker ์ปจํ ์ด๋ ๋น๋ ๋ฐ ์คํ
์ ์ฅ์๋ฅผ ๋ณต์ ํฉ๋๋ค.
git clone github.com/GoogleCloudPlatform/reCAPTCHA-PLD
์ปจํ ์ด๋๋ฅผ ๋น๋ํฉ๋๋ค.
docker build . -t pld-local
์ปจํ ์ด๋๋ฅผ ์์ํฉ๋๋ค.
docker run --network host \ -e RECAPTCHA_PROJECT_ID=PROJECT_ID \ -e GOOGLE_CLOUD_API_KEY=API_KEY \ pld-local
์ปจํ ์ด๋๊ฐ ์์๋๊ณ localhost์ ํฌํธ 8080์์ ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์์ํฉ๋๋ค.
localhost ์์ฒญ ์ ์ก
์์ฒญ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ ์ ๋ค์์ ๋ฐ๊ฟ๋๋ค.
- LEAKED_USERNAME: ์ ์ถ๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ์์ ์ฌ์ฉ์ ์ด๋ฆ์ ๋๋ค.
- LEAKED_PASSWORD: ์ ์ถ๋ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด ์์ ๋น๋ฐ๋ฒํธ์ ๋๋ค.
HTTP ๋ฉ์๋ ๋ฐ URL:
POST http://localhost:8080/createAssessment/
JSON ์์ฒญ ๋ณธ๋ฌธ:
{ "username":"LEAKED_USERNAME", "password":"LEAKED_PASSWORD" }
์์ฒญ์ ๋ณด๋ด๋ ค๋ฉด ๋ค์ ์ต์ ์ค ํ๋๋ฅผ ์ ํํฉ๋๋ค.
curl
์์ฒญ ๋ณธ๋ฌธ์ request.json
ํ์ผ์ ์ ์ฅํ๊ณ ๋ค์ ๋ช
๋ น์ด๋ฅผ ์คํํฉ๋๋ค.
curl -X POST \
-H "Content-Type: application/json; charset=utf-8" \
-d @request.json \
"http://localhost:8080/createAssessment/"
PowerShell
์์ฒญ ๋ณธ๋ฌธ์ request.json
ํ์ผ์ ์ ์ฅํ๊ณ ๋ค์ ๋ช
๋ น์ด๋ฅผ ์คํํฉ๋๋ค.
$headers = @{ }
Invoke-WebRequest `
-Method POST `
-Headers $headers `
-ContentType: "application/json; charset=utf-8" `
-InFile request.json `
-Uri "http://localhost:8080/createAssessment/" | Select-Object -Expand Content
๋ค์๊ณผ ๋น์ทํ JSON ์๋ต์ด ํ์๋ฉ๋๋ค.
{ "leakedStatus":"LEAKED" } OR { "leakedStatus":"NO_STATUS" }
๊ฒฐ๊ณผ ํด์ ๋ฐ ์กฐ์น
ํ๊ฐ ์๋ต์ ์ฌ์ฉ์ ์ธ์ฆ ์ ๋ณด๊ฐ ์ ์ถ๋์๋์ง ํ์ธํ๊ณ ์ฌ์ฉ์๋ฅผ ๋ณดํธํ๊ธฐ ์ํด ์ ์ ํ ์กฐ์น๋ฅผ ์ทจํ๋ ๋ฐ ์ฌ์ฉํ ์ ์๋ ์ ๋ณด๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋ค์ ํ์๋ ์ ์ถ๋ ๋น๋ฐ๋ฒํธ๊ฐ ๊ฐ์ง๋์์ ๋ ์ทจํ ์ ์๋ ๊ถ์ฅ ์กฐ์น๊ฐ ๋์ ์์ต๋๋ค.
์ ์ถ๋ ๋น๋ฐ๋ฒํธ ๊ฐ์ง๋จ | ์ฌ์ฉ์ ๋ณดํธ๋ฅผ ์ํ ์กฐ์น |
---|---|
๋ก๊ทธ์ธ ์ค |
|
๊ณ์ ์์ฑ ๋๋ ๋น๋ฐ๋ฒํธ ์ฌ์ค์ ์ค |
|
์ฌ์ดํธ์์ ์์ง MFA ์ ๊ณต์ ์ฒด๋ฅผ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ reCAPTCHA์ MFA ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋ค์ ๋จ๊ณ
- ๋ค์ค ์ธ์ฆ(MFA) ์ฌ์ฉ ๋ฐฉ๋ฒ ์์๋ณด๊ธฐ
- reCAPTCHA ๊ณ์ ๋ฐฉ์ด ๋๊ตฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ๊ณ์ ์ ๋ณดํธํ๋ ๋ฐฉ๋ฒ ์์๋ณด๊ธฐ