RSA 암호화/복화화 시 실시간으로 KEY 조합이어야 할 경우에는 어떻게 접근해야 할까?
실시간이라는 의미는 매번 접속할 때마다 RSA 암호화/복화화 KEY가 달라진다는 것이다.
HDD 에 저장된 Public KEY, Private KEY 가 아니라 메모리 상에서 접속시마다 생성하는 KEY 쌍이다.
최근의 보안검증에서는 이런 걸 요구하고 있다.
해킹 시도를 원천 차단하고자 하는 것이 목적이기 때문일 것이다.
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
|
<?php
if(!isset($_SESSION)) {
session_start();
}
require_once 'path.php';// root 폴더를 기준으로 상대적인 경로 자동 구하기
require_once $g['path_root'].'deviceChk.php';
require_once $g['path_config'].'config.php';
require_once $g['path_class'].'dbconnect.php';
require_once $g['path_class'].'loginClass.php';
$c = new LoginClass();
$salt = sha1(rand());
$salt = substr($salt, 0, 20);
$key = $c->rsa_generate_keys($salt);
$pubkey = $c->rsa_publickey($key['pub_key']);
$prikey = $c->rsa_privatekey($key['pri_key']);
$_SESSION['prikey'] = $prikey;
$_SESSION['salt'] = $salt;
// JSON 응답으로 공개키 전달
header('Content-Type: application/json');
echo json_encode(["pub_key" => $pubkey]);
/*
echo json_encode([
"pub_key" => $pubkey,
"session_id" => session_id(),
"prikey" => isset($_SESSION['prikey']) ? $_SESSION['prikey'] : "NOT_SET",
"salt" => isset($_SESSION['salt']) ? $_SESSION['salt'] : "NOT_SET",
]);
// */
?>
|
위 코드와 같이 PHP SESSION 으로 Private KEY를 전달한다.
KEY 쌍을 생성할 때 salt 를 적용하면 변화가 더욱 심해진다.
이런 점을 감안해서 JMeter 에서 암호화 로그인을 성공하기 위한 과정이다.
처음부터 부하를 많이 주는 테스트를 하면 안되고 로그인까지 정상적으로 되는지 확인해야 하기 때문에 1로 설정한다.
JSON Path expression 을 왜 이렇게 설정했는지는 위 PHP 소스코드를 보면 알 수 있다.
만약 data 배열 하단에 pub_key 로 생성하면 $.data.pub_key 로 변경해줘야 한다.
PHP SESSION 을 변수에 담아서 저장하기 위한 과정이다.
매번 HTTP Request 추가하여 실행될 때 마다 다른 PHPSESSID 가 생성될 수 있으니 필요한 곳에 값을 전달하면 된다.
추출한 PHP SESSION 값을 변수에 저장하는 과정이다.
공개키 가져오는 과정이다. JSR223 PreProcess 를 사용하여 변수를 가져와 암호화된 비밀번호를 생성할 수 있다.
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 java.security.KeyFactory
import java.security.spec.X509EncodedKeySpec
import java.security.PublicKey
import javax.crypto.Cipher
import org.apache.commons.codec.binary.Base64
// JSON Extractor에서 추출한 공개 키 가져오기
String publicKeyString = vars.get("pubkey");
// Debugging: JMeter log에서 확인 가능
log.info("Extracted Public Key: " + publicKeyString);
if (publicKeyString == null || publicKeyString.equals("NOT_FOUND") || publicKeyString.isEmpty()) {
throw new RuntimeException("Public Key not found in response! Check JSON Extractor.");
}
// 암호화할 비밀번호 가져오기
String password = vars.get("password");
// 공개 키 복원
byte[] keyBytes = Base64.decodeBase64(publicKeyString);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(spec);
// RSA 암호화 수행
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedBytes = cipher.doFinal(password.getBytes("UTF-8"));
// Base64로 인코딩
String encryptedPassword = Base64.encodeBase64String(encryptedBytes);
// JMeter 변수 저장
vars.put("encryptedPassword", encryptedPassword);
log.info("Encrypted Password: " + encryptedPassword);
|
로그인할 때 userID와 password 를 앞에서 생성한 encryptedPassword 를 입력변수로 전달한다.
Front-End 단의 코드는 필요없고, Back-End 단의 PHP 가 POST 변수로 받는 부분을 고려하면 된다.
Header 메시지는 웹브라우저의 개발자모드에서 확인한 사항을 적어둔다.
Cookie 에 앞에서 추출한 PHPSESSID를 변수로 적어준다.
토큰 추출 관련 PHP 소스코드
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
<?php
if(!isset($_SESSION)) {
session_start();
}
error_reporting(0);
header('Content-Type: application/json');
/*
echo json_encode([
"session_id" => session_id(),
"prikey" => isset($_SESSION['prikey']) ? $_SESSION['prikey'] : "NOT_SET",
"salt" => isset($_SESSION['salt']) ? $_SESSION['salt'] : "NOT_SET",
]);
exit;
// */
// jwt 라이브러리 사용
use \Firebase\JWT\JWT;
// 파일을 직접 실행하는 비정상적 동작을 방지 하기 위한 목적
if(isset($_POST) && $_SERVER['REQUEST_METHOD'] == "POST"){
@extract($_POST);
if(isset($userID) && !empty($userID) && isset($password) && !empty($password)) {
require_once 'path.php';
require_once $g['path_config'].'config.php';
require_once $g['path_class'].'dbconnect.php';
require_once $g['path_class'].'loginClass.php';
require_once $g['path_root'].'vendor/autoload.php';
$c = new LoginClass();
$private_key = $_SESSION['prikey'];
$salt = $_SESSION['salt'];
$password = $c->rsa_decrypt_key($password, $private_key,$salt); // RSA 패스워드 복호화 기능 구현
$rs = $c->LoginSuccessChk($userID,$password); // 개발 서버
$rs = (int)$rs;
switch($rs){
case 11:
// 로그인 허용
$user = $c->getUser($userID, $password);
if ($user != false) {
$issuedAt = time();
$expirationTime = $issuedAt + 3600; // 1시간 유효
if($user['admin']==1 || $user['admin']==2) {
$payload = array(
'iat' => $issuedAt,
"exp" => $expirationTime,
"userID" => $user['userID'],
"userNM" => $user['userNM'],
"access" => $user['access'],
"usrIDX" => $user['idx'],
"orgId" => $user['orgId'],
"authID" => $user['admin'],
);
$rs = 21;
} else {
$payload = array(
'iat' => $issuedAt,
"exp" => $expirationTime,
"userID" => $user['userID'],
"userNM" => $user['userNM'],
"access" => $user['access'],
"usrIDX" => $user['idx'],
"orgId" => $user['orgId'],
);
}
// JWT 생성
$token = JWT::encode($payload, SECRET_KEY, ALGORITHM);
// 세션 및 쿠키 저장
//$_SESSION['token'] = $token;
setcookie('token', $token, time() + 3600, '/'); // 1시간 동안 쿠키 저장
header('Content-Type: application/json');
echo json_encode(array('token' => $token));
} else {
echo json_encode(array('result' => '-3')); // 체크 필요
}
break;
default:
echo json_encode(array('result' => $rs));
break;
}
} else {// 입력받은 데이터에 문제가 있을 경우
echo json_encode(array('result' => '-2'));
}
} else { // 비정상적인 접속인 경우
echo json_encode(array('result' => '-3')); // loginChk.php 파일을 직접 실행할 경우에는 화면에 0을 찍어준다.
exit;
}
?>
|
로그인 이후의 파일에 접근하는 과정 설명 그림이다.
이 정도면 충분한 설명은 되었다고 본다.
소스코드를 분석하면서 JMeter 스크립트 과정을 작성해야 하는 거 같다.
본문내에 광고가 떠서 가독성을 떨어뜨리는 거 같아서 광고를 삭제시켰다.
블로그 접속빈도가 떨어져서 광고를 클릭할 가능성도 매우 낮다고 보기 때문이고, 정보 전달의 목적에 충실하자는 의도도 있다.
'Web 프로그램 > JMeter' 카테고리의 다른 글
JMeter RSA 암호화 로그인 방법 (2) | 2025.02.21 |
---|---|
JMeter token 인증 로그인 및 그 이후 설정 (0) | 2025.02.19 |