HTTP basic authentication#
기본 인증(Basic Authentication)은 웹 애플리케이션과 API에서 사용되는 가장 단순하고 오래된 HTTP 인증 방식 중 하나이다. 이 인증 방식은 1996년에 발표된 HTTP/1.0 명세의 일부로 처음 소개되었으며, 현재까지도 많은 시스템에서 활용되고 있다. 간단한 구조와 광범위한 지원으로 인해 여전히 중요한 인증 메커니즘으로 남아 있다.
기본 인증의 작동 원리#
기본 인증은 매우 직관적인 프로세스를 따른다:
요청 시도: 클라이언트가 보호된 리소스에 접근을 시도한다.
인증 요구: 서버는 리소스가 보호되어 있음을 인식하고 상태 코드 401 (Unauthorized)와 함께 응답한다. 이 응답에는 다음과 같은 헤더가 포함된다.
1
| WWW-Authenticate: Basic realm="접근 영역 설명"
|
여기서 “realm"은 보호 영역을 설명하는 문자열로, 사용자에게 어떤 자격 증명을 사용해야 하는지 힌트를 제공한다.
자격 증명 제공: 클라이언트는 사용자 이름과 비밀번호를 콜론(:)으로 결합하고, Base64로 인코딩하여 다음 형식의 Authorization 헤더를 요청에 포함시킨다:
1
| Authorization: Basic {Base64(username:password)}
|
인증 검증: 서버는 Authorization 헤더에서 Base64 인코딩된 문자열을 디코딩하여 사용자 이름과 비밀번호를 추출하고, 이를 저장된 자격 증명과 비교하여 인증을 검증한다.
접근 허용 또는 거부: 자격 증명이 유효하면 서버는 요청을 처리하고 응답을 반환한다. 유효하지 않으면 다시 401 상태 코드를 반환한다.
기본 인증의 구현 예시#
클라이언트 측 구현#
브라우저를 통한 기본 인증#
브라우저는 기본 인증을 기본적으로 지원한다. 서버가 401 응답을 반환하면, 브라우저는 자동으로 사용자 이름과 비밀번호를 요청하는 대화 상자를 표시한다.
프로그래밍 방식의 기본 인증 (JavaScript)#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| // 사용자 이름과 비밀번호를 Base64로 인코딩
function encodeCredentials(username, password) {
return btoa(`${username}:${password}`);
}
// 기본 인증을 사용한 API 요청
async function fetchWithBasicAuth(url, username, password) {
const encodedCredentials = encodeCredentials(username, password);
try {
const response = await fetch(url, {
headers: {
'Authorization': `Basic ${encodedCredentials}`
}
});
if (!response.ok) {
if (response.status === 401) {
throw new Error('인증 실패: 사용자 이름 또는 비밀번호가 잘못되었습니다.');
}
throw new Error(`요청 실패: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('요청 오류:', error);
throw error;
}
}
// 사용 예시
fetchWithBasicAuth('https://api.example.com/resource', 'username', 'password')
.then(data => console.log('데이터:', data))
.catch(error => console.error('오류:', error));
|
서버 측 구현#
Node.js/Express 서버에서의 기본 인증#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
| const express = require('express');
const app = express();
// 기본 인증 미들웨어
function basicAuth(req, res, next) {
// Authorization 헤더 확인
const authHeader = req.headers.authorization;
if (!authHeader) {
// 인증 헤더가 없으면 인증 요구
res.setHeader('WWW-Authenticate', 'Basic realm="접근하려면 로그인하세요"');
return res.status(401).send('인증이 필요합니다');
}
// Basic 인증 스키마 확인
if (!authHeader.startsWith('Basic ')) {
return res.status(401).send('지원되지 않는 인증 방식입니다');
}
// Base64 인코딩된 자격 증명 디코딩
const base64Credentials = authHeader.split(' ')[1];
const credentials = Buffer.from(base64Credentials, 'base64').toString('utf-8');
const [username, password] = credentials.split(':');
// 자격 증명 검증 (실제로는 데이터베이스 등에서 검증)
if (username === 'admin' && password === 'secret') {
// 인증 성공 시 요청 객체에 사용자 정보 추가
req.user = { username };
next();
} else {
// 인증 실패
res.setHeader('WWW-Authenticate', 'Basic realm="접근하려면 로그인하세요"');
res.status(401).send('잘못된 사용자 이름 또는 비밀번호');
}
}
// 모든 라우트에 기본 인증 적용
app.use(basicAuth);
// 보호된 라우트
app.get('/api/protected', (req, res) => {
res.json({
message: '인증된 접근 성공',
user: req.user.username
});
});
app.listen(3000, () => {
console.log('서버가 3000번 포트에서 실행 중입니다');
});
|
Apache 웹 서버에서의 기본 인증 설정#
Apache에서는 .htaccess
파일을 사용하여 기본 인증을 설정할 수 있다:
1
2
3
4
5
| # 기본 인증 활성화
AuthType Basic
AuthName "제한된 영역"
AuthUserFile /path/to/.htpasswd
Require valid-user
|
그리고 htpasswd
도구를 사용하여 비밀번호 파일을 생성할 수 있다:
1
| htpasswd -c /path/to/.htpasswd username
|
기본 인증의 장점#
- 단순성: 구현이 매우 간단하고 직관적.
- 호환성: 모든 브라우저와 HTTP 클라이언트에서 지원.
- 표준화: HTTP 프로토콜의 공식 부분으로, 널리 채택되고 이해되고 있다.
- 무상태(Stateless): 각 요청에 인증 정보가 포함되므로 서버 측에서 세션 상태를 유지할 필요가 없다.
- 프록시 및 중개자 호환성: 대부분의 프록시와 HTTP 중개자가 이 방식을 이해하고 처리할 수 있다.
기본 인증의 단점 및 보안 위험#
- 자격 증명 전송: 비밀번호가 요청마다 전송되며, Base64 인코딩은 암호화가 아닌 인코딩 방식이므로 중간자 공격에 취약하다.
- HTTPS 필수: 보안을 위해서는 반드시 HTTPS와 함께 사용해야 한다.
- 자격 증명 저장: 브라우저는 일반적으로 사용자 세션 동안 자격 증명을 저장하므로, 공유 컴퓨터에서는 보안 위험이 있다.
- 로그아웃 메커니즘 부재: 표준화된 로그아웃 메커니즘이 없어, 브라우저를 닫거나 다른 자격 증명으로 인증하는 방법밖에 없다.
- 제한된 보안 기능: 다단계 인증, 접근 제어 목록, 토큰 갱신 등의 고급 보안 기능을 제공하지 않는다.
- 브루트 포스 공격에 취약: 요청 제한이나 계정 잠금 메커니즘이 없으면 무차별 대입 공격에 취약하다.
보안 강화 방법#
기본 인증을 사용할 때 취할 수 있는 보안 강화 조치:
HTTPS 필수 사용: 모든 인증 통신은 TLS/SSL을 통해 암호화되어야 한다.
강력한 비밀번호 정책: 복잡하고 길며 예측하기 어려운 비밀번호를 요구한다.
요청 제한(Rate Limiting): 특정 IP 또는 사용자의 과도한 인증 시도를 제한한다.
보안 헤더 추가: 추가적인 HTTP 보안 헤더를 사용하여 중간자 공격, 클릭재킹 등을 방지한다.
1
2
3
| Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
|
접근 제한: 기본 인증을 특정 IP 범위나 내부 네트워크에서만 사용하도록 제한한다.
기본 인증의 실제 사용 사례#
기본 인증은 다음과 같은 상황에서 적절하게 사용될 수 있다:
- 내부 도구 및 관리 인터페이스: 조직 내부에서만 사용되는 도구나 관리 페이지.
- 개발 환경 및 스테이징 서버: 공개되지 않은 개발 중인 웹사이트나 API.
- 간단한 API 액세스: 복잡한 인증이 필요하지 않은 간단한 API.
- 레거시 시스템 통합: 현대적인 인증 방식을 지원하지 않는 오래된 시스템과의 통합.
- 프록시 서버 인증: 리버스 프록시에서의 간단한 인증 계층.
코드 예시: 다양한 언어에서의 기본 인증 구현#
Python (Flask)에서의 기본 인증 구현#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| from flask import Flask, request, Response
import base64
app = Flask(__name__)
# 사용자 데이터베이스 (실제로는 DB 사용)
USERS = {'admin': 'password'}
def check_auth(username, password):
"""사용자 이름과 비밀번호 검증"""
return username in USERS and USERS[username] == password
def authenticate():
"""인증 요구 응답 생성"""
return Response(
'Could not verify your access level for that URL.\n'
'You have to login with proper credentials', 401,
{'WWW-Authenticate': 'Basic realm="Login Required"'})
def requires_auth(f):
"""기본 인증을 요구하는 데코레이터"""
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
return authenticate()
return f(*args, **kwargs)
return decorated
@app.route('/api/protected')
@requires_auth
def protected_resource():
"""인증이 필요한 보호된 리소스"""
return {'message': '인증 성공!', 'user': request.authorization.username}
if __name__ == '__main__':
app.run(debug=True)
|
Java (Spring Boot)에서의 기본 인증 구현#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeRequests(authorizeRequests ->
authorizeRequests
.antMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.httpBasic();
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("admin")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW") // "password"
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user);
}
}
|
실제 사용 시나리오: 기본 인증의 현대적 적용#
CI/CD 파이프라인에서의 적용#
젠킨스(Jenkins), 깃랩(GitLab) 등의 CI/CD 도구는 종종 기본 인증을 사용하여, 빌드 파이프라인의 특정 웹훅이나 API 엔드포인트를 보호한다. 이는 간단하면서도 효과적인 방법으로, 내부 시스템 간의 통신에 적합하다.
1
2
3
| # Jenkins 웹훅 호출 예시
curl -X POST https://jenkins.example.com/job/build-project/build \
-u username:api_token
|
개발 및 스테이징 환경 보호#
개발 중인 웹사이트나 애플리케이션은 종종 기본 인증을 사용하여 무단 접근으로부터 보호된다. 이는 개발 중인 기능이 공개되거나 검색 엔진에 인덱싱되는 것을 방지한다.
1
2
3
4
5
6
7
8
9
10
11
12
| # Nginx 설정 예시
server {
listen 80;
server_name dev.example.com;
auth_basic "Development Environment";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://localhost:3000;
}
}
|
API 키 전송#
일부 API는 API 키를 사용자 이름으로, 비밀 값을 비밀번호로 사용하는 기본 인증 방식을 통해 간단한 API 키 인증을 구현한다.
1
2
3
4
5
6
7
| # Python에서 API 키를 사용한 기본 인증 요청
import requests
response = requests.get(
'https://api.example.com/data',
auth=('api_key_12345', '') # 비밀번호 부분은 빈 문자열
)
|
기본 인증의 현대적 대안#
현대 웹 애플리케이션에서 기본 인증은 점차 다음과 같은 방식으로 대체되고 있다:
- Bearer 토큰 인증: JWT(JSON Web Tokens)와 같은 서명된 토큰을 사용한 인증 방식.
- OAuth 2.0: 서비스 간 권한 위임을 위한 표준 프로토콜.
- API 키: 특히 서버 간 통신에서 널리 사용된다.
- HMAC 인증: 요청 메시지의 해시 기반 메시지 인증 코드를 사용하는 방식.
- SSO(Single Sign-On): 사용자가 한 번의 인증으로 여러 서비스에 접근할 수 있게 해주는 인증 시스템.
용어 정리#
참고 및 출처#