CAPTCHA 탐색적 테스트
GitLab v19.1리뷰 앱과 로컬 개발 환경(GDK)에서 CAPTCHA를 안정적으로 테스트할 수 있습니다. reCAPTCHA가 지원되는 곳에서 강제로 reCAPTCHA를 표시합니다. 도로 표지판 이미지를 찾아 선택하는 방식 대신 체크박스가 표시되도록 강제합니다.
리뷰 앱과 로컬 개발 환경(GDK)에서 CAPTCHA를 안정적으로 테스트할 수 있습니다. 다음 작업을 언제든지 수행할 수 있습니다:
-
reCAPTCHA가 지원되는 곳에서 강제로 reCAPTCHA를 표시합니다.
-
도로 표지판 이미지를 찾아 선택하는 방식 대신 체크박스가 표시되도록 강제합니다.
테스트를 설정하려면 이 페이지의 구성 방법을 따르세요.
적절한 테스트 데이터 사용#
스팸/CAPTCHA가 활성화된 시나리오를 테스트하고 있는지 확인하세요. 예를 들어, 공개 스니펫만 스팸 검사를 받으므로 공개 스니펫을 편집하고 있는지 확인하세요.
기능 플래그 활성화#
스팸/CAPTCHA 지원이 기능 플래그 뒤에 있는 경우, 관련 기능 플래그를 활성화하세요.
Akismet 및 reCAPTCHA 설정#
- reCAPTCHA를 설정하려면:
GitLab reCAPTCHA 문서를 검토하세요.
- Google이 제공하는 지침에 따라 공식 테스트 reCAPTCHA 자격 증명을 받으세요.
**사이트 키(Site key)**에는 다음을 사용합니다: 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
-
**비밀 키(Secret key)**에는 다음을 사용합니다:
6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe -
Admin > Settings > Reporting 설정으로 이동합니다:
http://gdk.test:3000/admin/application_settings/reporting#js-spam-settings -
Spam and Anti-bot Protection 섹션을 펼칩니다.
-
Enable reCAPTCHA를 선택합니다. 로그인 관련 기능을 테스트하는 것이 아니라면 로그인에 대한 활성화는 필요하지 않습니다.
-
Site key와 Secret key를 입력합니다.
-
Akismet을 설정하려면:
GitLab Akismet 문서를 검토하세요.
-
Akismet API 키를 받으세요. Akismet에서 테스트 키를 신청할 수 있습니다. 가입 시 로컬 호스트(예:
gdk.test)와 이메일을 입력해야 합니다. -
GitLab Akismet 설정 페이지로 이동합니다. 예를 들면:
http://gdk.test:3000/admin/application_settings/reporting#js-spam-settings -
Akismet을 활성화하고 Akismet API 키를 입력합니다.
-
Akismet 거짓 양성(false-positive) 스팸 검사를 강제하려면, Akismet API 문서와 Akismet 시작하기 문서를 참고하세요:
다음 단계에 따라 akismet-guaranteed-spam@example.com을 작성자 이메일로 사용하여 스팸을 강제할 수 있습니다:
사용자 이메일 설정으로 이동합니다: http://gdk.test:3000/-/profile/emails
-
관리자 사용자의 보조 이메일로
akismet-guaranteed-spam@example.com을 추가합니다. -
Rails 콘솔에서 확인합니다:
bin/rails c->User.find_by_username('root').emails.last.confirm -
이 인증된 이메일을 기본 이메일로 전환합니다:
아바타 드롭다운 목록 > Edit Profile > Main Settings로 이동합니다.
-
Email에
akismet-guaranteed-spam@example.com을 입력하여admin@example.com을 대체합니다. -
Update Profile Settings를 선택하여 변경 사항을 저장합니다.
웹 UI에서 테스트#
위의 모든 구성이 완료되면 CAPTCHA를 테스트할 수 있습니다. 다음과 같이 CAPTCHA 지원이 이미 있는 애플리케이션 영역에서 테스트하세요:
-
이슈 생성 또는 편집.
-
공개 스니펫 생성 또는 편집. 공개 스니펫만 스팸 검사를 받습니다.
개발 환경에서 테스트#
위의 단계를 사용하여 스팸 플래그 지정 + CAPTCHA를 강제한 후, 스팸으로 보호된 모든 모델/컨트롤러 액션에서 동작을 테스트할 수 있습니다.
CAPTCHA 활성화 상태에서 테스트 (CONDITIONAL_ALLOW 판정)#
이 영역에서 CAPTCHA가 활성화된 경우, 양식을 재제출하기 전에 CAPTCHA 팝업 모달을 풀어야 합니다:
-
Admin > Settings > Reporting > Spam
-
Anti-bot Protection > Enable reCAPTCHA
CAPTCHA 비활성화 상태에서 테스트 (DISALLOW 판정)#
Admin > Settings > Reporting > Spam 및 Anti-bot Protection > Enable reCAPTCHA에서 CAPTCHA가 비활성화된 경우, CAPTCHA 팝업이 표시되지 않습니다. 양식 제출이 완전히 차단됩니다.
reCAPTCHA를 렌더링하는 HTML 페이지#
Akismet 및 reCAPTCHA 설정에 나열된 Google 공식 테스트 reCAPTCHA 자격 증명을 사용하는 경우, CAPTCHA 응답 문자열은 중요하지 않습니다. 어떤 문자열이든 사용할 수 있습니다. 실제 유효한 키 쌍을 사용하는 경우, 유효한 CAPTCHA 응답을 얻기 위해 CAPTCHA를 직접 풀어야 합니다. 이는 한 번만 수행할 수 있으며, 만료되기 전에만 가능합니다.
GraphQL Explorer(http://gdk.test:3000/-/graphql-explorer)를 통해 GraphQL API를 직접 테스트하려면,
다음 양식을 통해 reCAPTCHA 응답 문자열을 가져오세요: public/recaptcha.html (http://gdk.test:3000/recaptcha.html):
<html>
<head>
<title>reCAPTCHA demo: Explicit render after an onload callback</title>
<script type="text/javascript">
var onloadCallback = function() {
grecaptcha.render('html_element', {
'sitekey' : '6Ld05AsaAAAAAMsm1yTUp4qsdFARN15rQJPPqv6i'
});
};
function onSubmit() {
window.document.getElementById('recaptchaResponse').innerHTML = grecaptcha.getResponse();
return false;
}
</script>
</head>
<body>
<form onsubmit="return onSubmit()">
<div id="html_element"></div>
<br>
<input type="submit" value="Submit">
</form>
<div>
<h1>recaptchaResponse:</h1>
<div id="recaptchaResponse"></div>
</div>
<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit"
async defer>
</script>
</body>
</html>
스팸/CAPTCHA API 탐색적 테스트 예시#
이 섹션에서는 REST 및 GraphQL API에서 스팸과 CAPTCHA 동작의 다양한 시나리오를 수동으로 탐색적 테스트하는 데 필요한 단계를 설명합니다.
사전 요구 사항으로 다음을 수행해야 합니다:
-
개발 환경에서 스팸 및 CAPTCHA를 활성화하고 양식 제출 시 CAPTCHA를 요구하도록 강제하기 위해 위에 나열된 모든 단계를 수행합니다.
-
/public디렉터리 아래에 CAPTCHA를 렌더링하는 HTML 페이지를 생성했는지 확인합니다. 이 페이지에는 유효한 CAPTCHA 응답 문자열을 수동으로 생성하는 양식이 포함되어야 합니다. Akismet 및 reCAPTCHA 설정에 나열된 Google 공식 테스트 reCAPTCHA 자격 증명을 사용하는 경우, CAPTCHA 응답 문자열의 내용은 중요하지 않습니다. -
Admin > Settings > Reporting > Spam and Anti-bot protection으로 이동합니다.
-
시나리오의 필요에 따라 Enable reCAPTCHA 및 Enable Akismet을 선택하거나 해제합니다.
다음 예시에서는 스니펫 생성을 예로 사용합니다. 스니펫 업데이트, 이슈 생성 또는 이슈 업데이트를 사용할 수도 있습니다. 이슈와 스니펫만 스팸 및 CAPTCHA를 완전히 지원하는 모델입니다.
초기 설정#
-
API 토큰을 생성합니다.
-
REST 명령에서 사용하기 위해 터미널에 내보냅니다:
export PRIVATE_TOKEN=<your_api_token> -
GraphQL 쿼리를 실행하기 위한 인증 사용자로 사용되므로, GraphiQL 탐색기를 사용하기 전에
localhost:3000의 GitLab 개발 환경에 로그인했는지 확인합니다. -
GraphQL 예시의 경우,
http://localhost:3000/-/graphql-explorer의 GraphiQL 탐색기를 사용합니다. -
상태 코드를 포함한 HTTP 응답 헤더를 출력하려면
curl에--include(-i) 옵션을 사용합니다.
시나리오: Akismet 및 CAPTCHA 활성화#
이 예시에서는 Akismet과 CAPTCHA가 활성화되어 있습니다:
초기 요청#
CAPTCHA 응답이 제공되지 않기 때문에 이 초기 요청은 실패합니다.
REST 요청:
curl --request POST --header "PRIVATE-TOKEN: $PRIVATE_TOKEN" "http://localhost:3000/api/v4/snippets?title=Title&file_name=FileName&content=Content&visibility=public"
REST 응답:
{"needs_captcha_response":true,"spam_log_id":42,"captcha_site_key":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX","message":{"error":"Your snippet has been recognized as spam. Please, change the content or solve the reCAPTCHA to proceed."}}
GraphQL 요청:
mutation {
createSnippet(input: {
title: "Title"
visibilityLevel: public
blobActions: [
{
action: create
filePath: "BlobPath"
content: "BlobContent"
}
]
}) {
snippet {
id
title
}
errors
}
}
GraphQL 응답:
{
"data": {
"createSnippet": null
},
"errors": [
{
"message": "Request denied. Solve CAPTCHA challenge and retry",
"locations": [
{
"line": 22,
"column": 5
}
],
"path": [
"createSnippet"
],
"extensions": {
"needs_captcha_response": true,
"spam_log_id": 140,
"captcha_site_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
}
]
}
두 번째 요청#
CAPTCHA 응답이 제공되었기 때문에 이 요청은 성공합니다.
REST 요청:
export CAPTCHA_RESPONSE=""
export SPAM_LOG_ID="<spam_log_id obtained from initial REST response>"
curl --request POST --header "PRIVATE-TOKEN: $PRIVATE_TOKEN" --header "X-GitLab-Captcha-Response: $CAPTCHA_RESPONSE" --header "X-GitLab-Spam-Log-Id: $SPAM_LOG_ID" "http://localhost:3000/api/v4/snippets?title=Title&file_name=FileName&content=Content&visibility=public"
REST 응답:
{"id":42,"title":"Title","description":null,"visibility":"public", "other_fields": "..."}
GraphQL 요청:
GitLab GraphiQL 구현은 헤더 전달을 허용하지 않으므로, 이를 curl 쿼리로 작성해야 합니다. 여기서는 JSON에 포함된 쿼리의 이스케이프된 큰따옴표를 올바르게 처리하기 위해 --data-binary를 사용합니다.
export CAPTCHA_RESPONSE=""
export SPAM_LOG_ID="<spam_log_id obtained from initial REST response>"
curl --include "http://localhost:3000/api/graphql" --header "Authorization: Bearer $PRIVATE_TOKEN" --header "Content-Type: application/json" --header "X-GitLab-Captcha-Response: $CAPTCHA_RESPONSE" --header "X-GitLab-Spam-Log-Id: $SPAM_LOG_ID" --request POST --data-binary '{"query": "mutation {createSnippet(input: {title: \"Title\" visibilityLevel: public blobActions: [ { action: create filePath: \"BlobPath\" content: \"BlobContent\" } ] }) { snippet { id title } errors }}"}'
GraphQL 응답:
{"data":{"createSnippet":{"snippet":{"id":"gid://gitlab/PersonalSnippet/42","title":"Title"},"errors":[]}}}
시나리오: Akismet 활성화, CAPTCHA 비활성화#
이 시나리오에서는 위에서 설명한 대로 Admin 영역 설정에서 Enable reCAPTCHA를 해제했는지 확인하세요. CAPTCHA가 활성화되지 않은 경우, 잠재적인 스팸으로 플래그가 지정된 요청은 재제출 기회 없이 실패합니다. CAPTCHA가 활성화되어 성공적으로 풀 수 있었다면 재제출이 가능했을 것입니다.
REST 요청은 CAPTCHA가 활성화된 경우와 동일합니다:
curl --request POST --header "PRIVATE-TOKEN: $PRIVATE_TOKEN" "http://localhost:3000/api/v4/snippets?title=Title&file_name=FileName&content=Content&visibility=public"
REST 응답:
{"message":{"error":"Your snippet has been recognized as spam and has been discarded."}}
GraphQL 요청:
mutation {
createSnippet(input: {
title: "Title"
visibilityLevel: public
blobActions: [
{
action: create
filePath: "BlobPath"
content: "BlobContent"
}
]
}) {
snippet {
id
title
}
errors
}
}
GraphQL 응답:
{
"data": {
"createSnippet": null
},
"errors": [
{
"message": "Request denied. Spam detected",
"locations": [
{
"line": 22,
"column": 5
}
],
"path": [
"createSnippet"
],
"extensions": {
"spam": true
}
}
]
}
시나리오: allow_possible_spam 애플리케이션 설정 활성화#
allow_possible_spam 애플리케이션 설정이 활성화된 경우, API는 200 응답을 반환합니다. 요청이 스팸으로 간주되더라도 유효한 요청은 모두 성공하며 CAPTCHA가 표시되지 않습니다.