├── .github
└── FUNDING.yml
├── .gitignore
├── README.md
├── v2
├── 128.png
├── 16.png
├── 48.png
├── background.js
├── loader.js
├── manifest.json
└── script.js
└── v3
├── 128.png
├── 16.png
├── 48.png
├── background.js
├── loader.js
├── manifest.json
└── script.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: Baw-Appie
2 | patreon: bawappie
3 | custom: ["https://paypal.me/pp121324", "https://l.bawappie.com/kakaopay"]
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Safe Transaction
2 |
3 | 여러분의 거래를 안전하게 지켜드리는 Safe Transaction 브라우저 확장 프로그램입니다.
4 |
5 | 표준 브라우저 확장을 따르도록 설계되어, 별도 프로그램 설치 없이 [@BawAppie](https://x.com/BawAppie)가 정한 한국 산업 표준 암호화를 이용하여
6 | 안전하게 아무 작업도 하지 않기 때문에 더욱 편리하게 사용할 수 있습니다. 물론 무슨 암호화인지는 모릅니다.
7 |
8 | 이 확장 프로그램을 설치하면 전자 금융 사기 예방 서비스가 가능하기 때문에 **무조건 설치**해야 합니다.
9 | 설치하고 싶지 않으세요? 그렇다면 대체 프로그램 설치 방법을 해당 사이트가 안내할 것입니다.
10 | 대체 프로그램과 행복한 시간이 되시길 바랍니다.
11 |
12 | > **경쟁 연구소로 부터 인정 받은 보안 기술**
13 | >
14 | > 
15 |
16 | # 사용 전 확인하세요.
17 | 이 확장 프로그램이 사용하는 서버는 KT, SK, LG U+ 등 국내 ISP에서 제공하는 DNS 서버에서 차단되었습니다.
18 | [한국인터넷진흥원의 DNS 간섭에 대해 조사하고 있습니다. #8](https://github.com/Baw-Appie/SafeTransaction/issues/8)
19 | 
20 | 개발자는 정부의 차단 시도에 대해 유감을 표합니다.
21 |
22 | ## 왜 차단되었을까요?
23 | KISA는 Safe Transaction 확장 프로그램이 보안 프로그램을 우회하기 때문에 공공의 위협을 초래할 수 있다고 합니다.
24 |
25 | ## 사실은 이렇습니다
26 | KISA의 주장과는 다르게 Safe Transaction은 보안 프로그램을 우회하지 않습니다.
27 | 아니 그렇다면 왜 이 확장 프로그램을 설치하면 보안 프로그램을 설치하라고 하지 않는 것일까요?
28 | Safe Transaction은 보안 프로그램보다 사용자 편의 중심의 보안 알고리즘을 사용한
29 | 더 강력한 보안 프로그램으로 대체하기 때문입니다.
30 | **여러분의 보안은 기존과 동일, 혹은 그 이상으로 안전하게 유지됩니다.**
31 |
32 | 내 컴퓨터에 프로그램을 설치하면 해당 프로그램은 컴퓨터의 권한을 모두 가져가게 됩니다.
33 | 하지만 Safe Transaction은 어떤가요? 브라우저 컨텍스트 안에서 격리되어 브라우저 밖을 빠져나올 수 없게 됩니다.
34 | 누군가 보안 프로그램의 취약점을 활용하여 내 컴퓨터를 해킹하는 이상한 사태가 발생할 수는 있어도,
35 | Safe Transaction의 취약점을 활용하여 해킹이 가능한 범위는 브라우저 탭, 그 이상은 불가합니다.
36 |
37 | 이러한 브라우저 격리 기술은 [WebKit](https://webkit.org), [Chromium](https://chromium.org), [Gecko](https://firefox-source-docs.mozilla.org/overview/gecko.html) 등을 통해 구현되어 [Apple](https://apple.com), [Google](https://google.com), [Microsoft](https://microsoft.com), [Mozilla](https://mozilla.org) 등
38 | 다양한 회사와 수 많은 자원 봉사자의 지원에 의하여 검증되었습니다.
39 | 그렇지만 보안 프로그램이라고 주장하는 것은 대체 누가 그 보안을 검증하였나요?
40 | 저는 모르겠습니다.
41 |
42 | ## 그래서 어떻게 할까요?
43 | 이 문제는 사실 개발자가 해결해야 할 문제입니다.
44 | 하지만 이러한 차단을 우회한다 하더라도 다시 차단된다면 불법 사이트가 도메인 바꾸는 것 마냥 저도 도메인 수백개를 사서 사용해야 하게 될 것입니다.
45 | 하지만 이 글을 보시고 계신 이용자님께서는 저에게 돈을 주지 않을 것을 알고 있습니다.
46 | 그렇기에 사용자 분들에게 근본적인 문제 해결이 가능한 [DNS 변경]을 권장드립니다.
47 |
48 | 위의 사유로 인하여 이 확장 프로그램을 사용하고자 한다면 네트워크의 DNS를 [Cloudflare DNS Resolver (1.1.1.1)](https://1.1.1.1) 또는 [Google Public DNS (8.8.8.8)](https://dns.google/) 등의 해외 서비스가 제공하는 DNS 서버로 변경하세요.
49 | 차단된 주소는 다음과 같습니다:
50 | - astx2-emulator.appie.dev
51 | - ipinside-emulator.appie.dev
52 |
53 | # 설치
54 |
55 | Chrome: https://chrome.google.com/webstore/detail/safe-transaction/hlknngdmefboagppfcddkbnnphbhdngk
56 | Firefox: https://addons.mozilla.org/addon/safe-transaction
57 | Whale: https://store.whale.naver.com/detail/fennejhonlpdndgjkhpgojjlnfbcjakk
58 |
59 | # 지원 현황
60 |
61 | | | 조회 | 거래(이체) | 비고 |
62 | | ------- | -- | ------ | ---------------------------------------------------- |
63 | | 국민은행 | ✔️ | ✔️ | KB국민인증서 로그인 |
64 | | 하나은행 | ✔️ | ✔️ | 하나인증서 로그인 |
65 | | 신한은행 | ✔️ | ✔️ | 신한인증서 로그인 |
66 | | 우리은행 | ❓ | ❓ | 확인 필요 |
67 | | 농협은행 | ✔️ | ✔️ | 금융인증서 로그인 |
68 | | 기업은행 | ✔️ | ✔️ | 금융인증서 로그인, 24.09.26 확인 |
69 | | SC제일은행 | ✔️ | ❌ | 금융인증서 로그인, INISAFE CrossWeb EX 설치시 이체 가능 |
70 | | 케이뱅크 기업 | ✔️ | ✔️ | 공동인증서 로그인, 25.05.22 케뱅비즈 전용 패치로 우회 |
71 | | OK저축은행 | ✔️ | ❌ | 금융인증서 로그인, 이체 시도시 NOS 통신 오류 (NOS E2E 필요) / 24.09.26 확인 |
72 | | 삼성증권[^삼성증권] | ✔️ | ❌ | 클라우드 인증서 로그인, SignKorea NA Certification Tool Kit 설치 필요 |
73 | [^삼성증권]: 클라우드 인증서 사용시 SignKorea NA Certification Tool Kit 설치가 되었음을 나타내는 Tampermonkey 스크립트로 로그인 가능함을 확인하였으나, 거래시 ASTx2의 E2E가 필요하여 거래 불가
74 |
75 | # 기능 및 특징
76 |
77 | - 안전한 거래를 강력하게 지원합니다.
78 | - IP 안에 무엇이 있을지 생각해보신 적 있으세요? 아무것도 없답니다.
79 | - 온라인 보안을 해드립니다
80 |
81 | # 만드는 것을 도와주신 감사한 분
82 |
83 | - [@BawAppie](https://twitter.com/BawAppie)
84 |
85 | ## Github Sponsor
86 | 이 프로젝트는 [한국모바일상품권](https://korgiftcard.io)으로부터 금전적인 지원을 많이 받았습니다.
87 |
88 |
89 | # 외부 서버 이용 안내
90 |
91 | 이 확장 프로그램은 외부 서버를 이용합니다. 그러므로 인터넷 없이는 작동하지 않습니다.
92 | 왜 개발자는 굳이 돈 주고 서버와 도메인을 구매한 뒤 외부 서버를 이용하도록 만들었을까요? 그 이유는 사용되는 암호화 알고리즘은 각 언어마다 사용 방법이 매우 다르며 기본 동작 마저 달라 구현을 옮기는 것이 쉽지 않기 때문입니다.
93 | Java 공화국, 대한민국인 만큼 구현에 사용한 암호화 PoC는 Kotlin JVM으로 작성되었으며 이러한 코드를 JavaScript로 다시 작성하는 것보다는, 암호화하는 서버를 만드는 것이 더욱 간단하고 쉬우며 개인 정보 유출에 더 큰 도움이 됩니다.
94 | 하지만 걱정할 필요가 없습니다. 이 확장 프로그램은 여러분의 개인정보를 수집하지 않습니다. 개발자는 여러분의 개인 정보를 존중하며 이 확장 프로그램이 사용하는 외부 서버는 여러분의 개인 정보를 저장하지 않습니다.
95 | 못 믿으시겠나요? 다시 생각해보세요. 이용자의 개인정보는 이미 공공재이므로 다른 곳에서 쉽게 구할 수 있는데 제가 뭐하러 수집을 하나요? 오히려 저장공간 낭비가 될 것입니다.
96 |
97 | 누군가는 저에게 그 정도 코드 옮기는 것은 그냥 AI로 '딸깍'하면 된다고 말하고는 합니다.
98 | 하지만 AI는 BouncyCastle이 얼마나 강력한 라이브러리인지 알고나 있을까요?
99 | 저는 BouncyCastle 없이 다시 구현하기가 두렵습니다.
100 |
101 |
102 | # 외부 서버의 소스 코드
103 |
104 | 이 개발자는 굳이 외부 서버를 사용하면서 외부 서버의 소스 코드를 공개하지 않았습니다.
105 | 왜일까요? 개발자의 입장에서 생각해봅시다. 이 확장 프로그램을 본 어떤 연구소는 이 확장 프로그램을 가만히 두지 않을 가능성이 높습니다. 그러므로 이 개발자는 나중에 이 확장 프로그램이 문제가 되었을 경우 도망가기 위하여 외부 서버를 공개하지 않았습니다. 개발자가 외부 서버를 닫아버리면 이 확장 프로그램은 고장나기 때문에 외부 서버는 킬 스위치의 역할을 할 수 있습니다.
106 | 하지만 개발자가 소스 코드를 공개했다면 어떨까요? 개발자가 서버를 닫더라도 누군가 서버를 다시 만들면 그만입니다. 저는 도망가야하기 때문에 공개하지 않겠습니다. 물론 도망가면 병역 면탈 또는 탈영으로 잡혀갑니다. 감사합니다.
107 |
108 |
109 | # 알고 있는 문제
110 |
111 | ## 1. 나는 MacOS 안쓰는데요?
112 | 보안 거래가 작동하면 그 사이트는 내가 macOS를 사용 중이라고 인식하여 이상한 파일을 던져주거나, 오작동하는 문제가 있을 수 있습니다.
113 | 이는 대부분의 사이트가 Windows에는 합법인척 하는 키로깅[1], 언제 터질지 모르는 커널 후킹 등 바이러스인지 뭔지 모를 괴랄한 짓을 하는 것에 비해 macOS는 간단한 짓만 하기 때문에 그런 것이니 참으시고 사용하세요.
114 |
115 | ## 2. 설치 후 유해 사이트라고 뜸
116 | 경쟁 제품에서 Safe Transaction이 사용하는 도메인을 피싱 사이트로 지정하여 발생하는 문제입니다.
117 | 왜 Safe Transaction과 경쟁 제품을 동시에 사용하시려고 하는지는 잘 모르겠지만, 해당 제품은 Safe Transaction를 보고 배가 아파서 정상 작동하지 못하도록 유해 사이트라는 거짓 작동을 하게 만든 것이 아닐까요?
118 | 위에서도 한번 언급했지만, Safe Transaction은 여러분의 민감한 데이터를 훔치치지 않습니다. 귀찮아서 끄지 않은 Nginx에 로깅되는 데이터에는 Cloudflare의 IP만 찍힐 뿐입니다.
119 | 저는 어떤 프로그램을 사용하는지는 사용하는 사람의 선택이니 어떤 것을 선택하도록 강요하지 않습니다. 그러므로 아래 몇 가지 해결 방법이 있습니다.
120 |
121 | ### 해결 방법 1: Safe Transaction 삭제
122 | 키로깅 기능부터, 사용중인 PC 정보 수집, 사용자 핑거프린팅, 접속 사이트 감청[1: 대한민국 해석 기준으로는 아님] 등 세계 최고 기능이 한 번에 포함된 프로그램을 Safe Transaction를 대신하여 사용하는 방법입니다.
123 | 수집을 동의하지 않으신거 같다고요? 걱정마세요. 경쟁사 개인정보 수집방침 깊숙한 곳에 숨겨져 있고, 따로 동의하라는 팝업을 띄우지도 않았거든요.[2]
124 |
125 | 이 글을 보는 사람이 이 방법을 사용하실 분은 없을 것이라고 생각합니다만, 아무튼 Safe Transaction을 지우면 문제가 해결됩니다.
126 | (Safe Transaction은 경쟁 제품과 달리 삭제시 클릭 한번으로 제거됩니다)
127 |
128 | ### 해결 방법 2: 경쟁 제품 삭제
129 | 누군지도 모르는 개발자가 적어둔 개인정보 수집하지 않는다는 글을 믿으시는 분에게 추천하는 방법입니다.
130 | 경쟁 제품을 삭제하면 해결됩니다. ([구라 제거기](https://teus.me/862?category=836336)를 활용하면 좋습니다)
131 |
132 | ### 해결 방법 3: ECH 사용
133 | 경쟁 제품은 http에서는 Host 헤더를, https에서는 [SNI 필드를 체크하여 유해 사이트 여부](https://brunch.co.kr/@searphiel9/46)를 확인합니다.
134 | https에서 이러한 감청을 막기 위한 여러가지의 시도들이 있었고 그 중 한 가지 방법으로 ECH(Encrypted Client Hello, [관련 링크](https://blog.cloudflare.com/encrypted-client-hello/))를 사용하는 방법입니다.
135 | Firefox에서 DoH를 활성화 한 뒤, about:config 로 이동하여 ```network.dns.echconfig.enabled```를 ```true```로 변경하면 됩니다.
136 | 대신 이 방법은 기본적으로 비활성화되어 있는 기능인 만큼 실험적이고 항상 잘 작동하진 않습니다.
137 |
138 | # 님만 모르는 문제
139 |
140 | 저는 착한 개발자이니 Safe Transaction을 사용함으로써 발생하는 모든 문제는 여러분에게 무상으로 제공하겠습니다. 사양하지 않으셔도 됩니다.
141 |
142 | [1] 대한민국 (유사) 유권해석 기준으로 처음부터 단대단 암호화가 아닌 제3자가 디코딩 후 열람 할 수 있는 사항을 보는 것은 "감청"에 해당되지 않으므로, 키로거는 "감청" 이 아닙니다.
143 | [2] 경쟁사 개발사인 모 연구소에서는 이런 프로그램을 PUP (Potentially Unwanted Program) 이라고 부르는 것 같아요.
144 |
--------------------------------------------------------------------------------
/v2/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Baw-Appie/SafeTransaction/da3c350f184a2cbcfa14cf48d36e4c889843a573/v2/128.png
--------------------------------------------------------------------------------
/v2/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Baw-Appie/SafeTransaction/da3c350f184a2cbcfa14cf48d36e4c889843a573/v2/16.png
--------------------------------------------------------------------------------
/v2/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Baw-Appie/SafeTransaction/da3c350f184a2cbcfa14cf48d36e4c889843a573/v2/48.png
--------------------------------------------------------------------------------
/v2/background.js:
--------------------------------------------------------------------------------
1 | browser.webRequest.onBeforeRequest.addListener(
2 | details => {
3 | const url = new URL(details.url);
4 | if(url.host === "127.0.0.1:55920" || url.host === "lx.astxsvc.com:55920") return { redirectUrl: "https://astx2-emulator.appie.dev" + url.pathname + url.search };
5 | if(url.host === "127.0.0.1:21300") return { redirectUrl: "https://ipinside-emulator.appie.dev" + url.pathname + url.search };
6 | if(url.host === "127.0.0.1:14440") return { redirectUrl: "https://nos-emulator.appie.dev" + url.pathname + url.search };
7 | },
8 | {
9 | urls: [""]
10 | },
11 | ["blocking"]
12 | )
--------------------------------------------------------------------------------
/v2/loader.js:
--------------------------------------------------------------------------------
1 | var s = document.createElement('script');
2 | s.src = chrome.runtime.getURL('script.js');
3 | s.onload = function() {
4 | this.remove();
5 | };
6 | (document.head || document.documentElement).appendChild(s);
--------------------------------------------------------------------------------
/v2/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Safe Transaction",
3 | "description" : "Secure your transaction",
4 | "version": "1.1.1",
5 | "manifest_version": 2,
6 | "icons": {
7 | "16": "16.png",
8 | "48": "48.png",
9 | "128": "128.png"
10 | },
11 | "permissions": [
12 | "webRequest",
13 | "",
14 | "https://lx.astxsvc.com:55920/*",
15 | "https://127.0.0.1:55920/*",
16 | "https://127.0.0.1:21300/*",
17 | "https://127.0.0.1:14440/*",
18 | "webRequestBlocking"
19 | ],
20 | "web_accessible_resources": ["script.js"],
21 | "content_scripts": [
22 | {
23 | "run_at": "document_start",
24 | "matches": [""],
25 | "js": ["loader.js"],
26 | "all_frames": true
27 | }
28 | ],
29 | "background": {
30 | "scripts": ["background.js"]
31 | }
32 | }
--------------------------------------------------------------------------------
/v2/script.js:
--------------------------------------------------------------------------------
1 | const _SafeTransactionNOSVKeypadPatchInitializer = () => {
2 | const callback = async (mutationList, observer) => {
3 | for (const mutation of mutationList) {
4 | if (mutation.type === "attributes" && mutation.attributeName == "class" && mutation.target.classList.contains("nppfs-npv")) {
5 | const { target } = mutation
6 | window.npVCtrl.hideAll()
7 | target.classList.remove("nppfs-npv")
8 | const newTarget = target.cloneNode(true)
9 | target.parentNode.replaceChild(newTarget, target);
10 |
11 | const encryptedInput = document.getElementsByName(`__KI_${target.name}`)[0]
12 | const makeKI = await fetch("https://127.0.0.1:14440/_/makeKI", { method: "POST", body: encryptedInput.value })
13 | const response = await makeKI.text()
14 | const splitResponse = response.split("|")
15 | encryptedInput.value = splitResponse[0]
16 | newTarget.removeAttribute("readonly")
17 | newTarget.setAttribute("x-safetransaction-kh", splitResponse[1])
18 | newTarget.addEventListener("blur", onblurCallback)
19 | console.log("[SafeTransaction] NOS Virtual Keypad Replaced", newTarget)
20 | }
21 | }
22 | }
23 | const observer = new MutationObserver(callback);
24 | observer.observe(document, { attributes: true, childList: true, subtree: true });
25 |
26 | const onblurCallback = async e => {
27 | const { target } = e
28 | const kh = target.getAttribute("x-safetransaction-kh")
29 | const khInput = document.getElementsByName(`__KH_${kh}`)[0]
30 | khInput.value = target.value
31 | }
32 |
33 | // const npkencryptInputs = document.querySelectorAll('input[npkencrypt]');
34 | // if(npkencryptInputs.length == 0) {
35 | // const fakeNuaGetter = () => "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36"
36 | // Object.defineProperty(window, "nua", { get: fakeNuaGetter });
37 | // }
38 | }
39 |
40 | const _SafeTransactionUserAgentPatcher = () => {
41 | let needToPatch = false
42 | if (window.$ASTX2) needToPatch = true
43 | if (needToPatch) {
44 | const fakePlatformGetter = () => "MacIntel"
45 | if (Object.defineProperty) {
46 | Object.defineProperty(navigator, "platform", { get: fakePlatformGetter });
47 | Object.defineProperty(Navigator.prototype, "platform", { get: fakePlatformGetter });
48 | } else if (Object.prototype.__defineGetter__) {
49 | navigator.__defineGetter__("platform", fakePlatformGetter);
50 | Navigator.prototype.__defineGetter__("platform", fakePlatformGetter);
51 | }
52 | const fakeUserAgentGetter = () => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
53 | if (Object.defineProperty) {
54 | Object.defineProperty(navigator, "userAgent", { get: fakeUserAgentGetter });
55 | Object.defineProperty(Navigator.prototype, "userAgent", { get: fakeUserAgentGetter });
56 | } else if (Object.prototype.__defineGetter__) {
57 | navigator.__defineGetter__("userAgent", fakeUserAgentGetter);
58 | Navigator.prototype.__defineGetter__("userAgent", fakeUserAgentGetter);
59 | }
60 | const fakeAppVersionGetter = () => "5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
61 | if (Object.defineProperty) {
62 | Object.defineProperty(navigator, "appVersion", { get: fakeAppVersionGetter });
63 | Object.defineProperty(Navigator.prototype, "appVersion", { get: fakeAppVersionGetter });
64 | } else if (Object.prototype.__defineGetter__) {
65 | navigator.__defineGetter__("appVersion", fakeAppVersionGetter);
66 | Navigator.prototype.__defineGetter__("appVersion", fakeAppVersionGetter);
67 | }
68 | }
69 | }
70 |
71 | const _SafeTransactionKBankBizPatcher = () => {
72 | if (location.host != "biz.kbanknow.com") return
73 | Object.defineProperty(window, "SECURE_TYPE", { value: "", writable: false });
74 |
75 | const originalOpen = XMLHttpRequest.prototype.open;
76 | const originalSend = XMLHttpRequest.prototype.send;
77 |
78 | XMLHttpRequest.prototype.open = function (method, url) {
79 | this._url = url;
80 | return originalOpen.apply(this, arguments);
81 | };
82 |
83 | XMLHttpRequest.prototype.send = function () {
84 | if (typeof this._url === 'string' && this._url.startsWith("/product/initech/crossweb/webui/conf/customerConf.json")) {
85 | this.addEventListener('readystatechange', e => {
86 | if (this.readyState != 4) return
87 | const newText = this.responseText.replaceAll(`"USE":"Y"`, `"USE":"N"`)
88 | Object.defineProperty(this, 'responseText', { get: () => { return newText; } });
89 | Object.defineProperty(this, 'response', { get: () => { return newText; } });
90 | });
91 | }
92 | return originalSend.apply(this, arguments);
93 | };
94 | }
95 |
96 | const _SafeTransactionHomeTaxPatcher = () => {
97 | if (location.host != "hometax.go.kr") return
98 | const originalOpen = XMLHttpRequest.prototype.open;
99 | const originalSend = XMLHttpRequest.prototype.send;
100 |
101 | XMLHttpRequest.prototype.open = function (method, url) {
102 | this._url = url;
103 | return originalOpen.apply(this, arguments);
104 | };
105 |
106 | XMLHttpRequest.prototype.send = function () {
107 | if (typeof this._url === 'string' && this._url.includes("js/comm/raonsecure/transkey/rsa_oaep_files/rsa_oaep-min.js")) {
108 | console.log("[SafeTransaction] Patching XMLHttpRequest for HomeTax RSA OAEP");
109 | this.addEventListener('readystatechange', e => {
110 | if (this.readyState != 4) return
111 | Object.defineProperty(this, 'statusText', { get: () => { return "Internal Server Error"; } });
112 | Object.defineProperty(this, 'status', { get: () => { return 500; } });
113 | });
114 | }
115 | return originalSend.apply(this, arguments);
116 | };
117 | }
118 |
119 | // _SafeTransactionNOSVKeypadPatchInitializer()
120 | _SafeTransactionKBankBizPatcher()
121 | _SafeTransactionHomeTaxPatcher()
122 | const _SafeTransactionOnWindowLoaded = () => {
123 | _SafeTransactionUserAgentPatcher()
124 | }
125 |
126 |
127 | if (document.readyState === "complete" || document.readyState === "interactive") {
128 | setTimeout(_SafeTransactionOnWindowLoaded, 1);
129 | } else {
130 | document.addEventListener("DOMContentLoaded", _SafeTransactionOnWindowLoaded);
131 | }
--------------------------------------------------------------------------------
/v3/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Baw-Appie/SafeTransaction/da3c350f184a2cbcfa14cf48d36e4c889843a573/v3/128.png
--------------------------------------------------------------------------------
/v3/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Baw-Appie/SafeTransaction/da3c350f184a2cbcfa14cf48d36e4c889843a573/v3/16.png
--------------------------------------------------------------------------------
/v3/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Baw-Appie/SafeTransaction/da3c350f184a2cbcfa14cf48d36e4c889843a573/v3/48.png
--------------------------------------------------------------------------------
/v3/background.js:
--------------------------------------------------------------------------------
1 | chrome.runtime.onInstalled.addListener(() => {
2 | chrome.declarativeNetRequest.updateDynamicRules({
3 | addRules: [
4 | {
5 | id: 1,
6 | priority: 1,
7 | action: {
8 | type: 'redirect',
9 | redirect: { regexSubstitution: 'https://astx2-emulator.appie.dev/\\1' }
10 | },
11 | condition: {
12 | regexFilter: 'https://127.0.0.1:55920/(.*)',
13 | resourceTypes: ["script", "xmlhttprequest"]
14 | }
15 | },
16 | {
17 | id: 2,
18 | priority: 2,
19 | action: {
20 | type: 'redirect',
21 | redirect: { regexSubstitution: 'https://astx2-emulator.appie.dev/\\1' }
22 | },
23 | condition: {
24 | regexFilter: 'https://lx.astxsvc.com:55920/(.*)',
25 | resourceTypes: ["script", "xmlhttprequest"]
26 | }
27 | },
28 | {
29 | id: 3,
30 | priority: 3,
31 | action: {
32 | type: 'redirect',
33 | redirect: { regexSubstitution: 'https://ipinside-emulator.appie.dev/\\1' }
34 | },
35 | condition: {
36 | regexFilter: 'https://127.0.0.1:21300/(.*)',
37 | resourceTypes: ["script", "xmlhttprequest"]
38 | },
39 | },
40 | {
41 | id: 4,
42 | priority: 4,
43 | action: {
44 | type: 'redirect',
45 | redirect: { regexSubstitution: 'https://nos-emulator.appie.dev/\\1' }
46 | },
47 | condition: {
48 | regexFilter: 'https://127.0.0.1:14440/(.*)',
49 | resourceTypes: ["script", "xmlhttprequest", "image"]
50 | }
51 | },
52 | ],
53 | removeRuleIds: [1,2,3,4]
54 | })
55 | })
56 |
57 |
--------------------------------------------------------------------------------
/v3/loader.js:
--------------------------------------------------------------------------------
1 | var s = document.createElement('script');
2 | s.src = chrome.runtime.getURL('script.js');
3 | s.onload = function() {
4 | this.remove();
5 | };
6 | (document.head || document.documentElement).appendChild(s);
--------------------------------------------------------------------------------
/v3/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Safe Transaction",
3 | "description" : "Secure your transaction",
4 | "version": "1.1.1",
5 | "manifest_version": 3,
6 | "icons": {
7 | "16": "16.png",
8 | "48": "48.png",
9 | "128": "128.png"
10 | },
11 | "permissions": [
12 | "declarativeNetRequest"
13 | ],
14 | "host_permissions": [
15 | ""
16 | ],
17 | "web_accessible_resources": [{
18 | "resources": ["script.js"],
19 | "matches": [""]
20 | }],
21 | "content_scripts": [
22 | {
23 | "run_at": "document_start",
24 | "matches": [""],
25 | "js": ["loader.js"],
26 | "all_frames": true
27 | }
28 | ],
29 | "background": {
30 | "service_worker": "background.js"
31 | }
32 | }
--------------------------------------------------------------------------------
/v3/script.js:
--------------------------------------------------------------------------------
1 | const _SafeTransactionNOSVKeypadPatchInitializer = () => {
2 | const callback = async (mutationList, observer) => {
3 | for (const mutation of mutationList) {
4 | if (mutation.type === "attributes" && mutation.attributeName == "class" && mutation.target.classList.contains("nppfs-npv")) {
5 | const { target } = mutation
6 | window.npVCtrl.hideAll()
7 | target.classList.remove("nppfs-npv")
8 | const newTarget = target.cloneNode(true)
9 | target.parentNode.replaceChild(newTarget, target);
10 |
11 | const encryptedInput = document.getElementsByName(`__KI_${target.name}`)[0]
12 | const makeKI = await fetch("https://127.0.0.1:14440/_/makeKI", { method: "POST", body: encryptedInput.value })
13 | const response = await makeKI.text()
14 | const splitResponse = response.split("|")
15 | encryptedInput.value = splitResponse[0]
16 | newTarget.removeAttribute("readonly")
17 | newTarget.setAttribute("x-safetransaction-kh", splitResponse[1])
18 | newTarget.addEventListener("blur", onblurCallback)
19 | console.log("[SafeTransaction] NOS Virtual Keypad Replaced", newTarget)
20 | }
21 | }
22 | }
23 | const observer = new MutationObserver(callback);
24 | observer.observe(document, { attributes: true, childList: true, subtree: true });
25 |
26 | const onblurCallback = async e => {
27 | const { target } = e
28 | const kh = target.getAttribute("x-safetransaction-kh")
29 | const khInput = document.getElementsByName(`__KH_${kh}`)[0]
30 | khInput.value = target.value
31 | }
32 |
33 | // const npkencryptInputs = document.querySelectorAll('input[npkencrypt]');
34 | // if(npkencryptInputs.length == 0) {
35 | // const fakeNuaGetter = () => "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Mobile Safari/537.36"
36 | // Object.defineProperty(window, "nua", { get: fakeNuaGetter });
37 | // }
38 | }
39 |
40 | const _SafeTransactionUserAgentPatcher = () => {
41 | let needToPatch = false
42 | if (window.$ASTX2) needToPatch = true
43 | if (needToPatch) {
44 | const fakePlatformGetter = () => "MacIntel"
45 | if (Object.defineProperty) {
46 | Object.defineProperty(navigator, "platform", { get: fakePlatformGetter });
47 | Object.defineProperty(Navigator.prototype, "platform", { get: fakePlatformGetter });
48 | } else if (Object.prototype.__defineGetter__) {
49 | navigator.__defineGetter__("platform", fakePlatformGetter);
50 | Navigator.prototype.__defineGetter__("platform", fakePlatformGetter);
51 | }
52 | const fakeUserAgentGetter = () => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
53 | if (Object.defineProperty) {
54 | Object.defineProperty(navigator, "userAgent", { get: fakeUserAgentGetter });
55 | Object.defineProperty(Navigator.prototype, "userAgent", { get: fakeUserAgentGetter });
56 | } else if (Object.prototype.__defineGetter__) {
57 | navigator.__defineGetter__("userAgent", fakeUserAgentGetter);
58 | Navigator.prototype.__defineGetter__("userAgent", fakeUserAgentGetter);
59 | }
60 | const fakeAppVersionGetter = () => "5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
61 | if (Object.defineProperty) {
62 | Object.defineProperty(navigator, "appVersion", { get: fakeAppVersionGetter });
63 | Object.defineProperty(Navigator.prototype, "appVersion", { get: fakeAppVersionGetter });
64 | } else if (Object.prototype.__defineGetter__) {
65 | navigator.__defineGetter__("appVersion", fakeAppVersionGetter);
66 | Navigator.prototype.__defineGetter__("appVersion", fakeAppVersionGetter);
67 | }
68 | }
69 | }
70 |
71 | const _SafeTransactionKBankBizPatcher = () => {
72 | if (location.host != "biz.kbanknow.com") return
73 | Object.defineProperty(window, "SECURE_TYPE", { value: "", writable: false });
74 |
75 | const originalOpen = XMLHttpRequest.prototype.open;
76 | const originalSend = XMLHttpRequest.prototype.send;
77 |
78 | XMLHttpRequest.prototype.open = function (method, url) {
79 | this._url = url;
80 | return originalOpen.apply(this, arguments);
81 | };
82 |
83 | XMLHttpRequest.prototype.send = function () {
84 | if (typeof this._url === 'string' && this._url.startsWith("/product/initech/crossweb/webui/conf/customerConf.json")) {
85 | this.addEventListener('readystatechange', e => {
86 | if (this.readyState != 4) return
87 | const newText = this.responseText.replaceAll(`"USE":"Y"`, `"USE":"N"`)
88 | Object.defineProperty(this, 'responseText', { get: () => { return newText; } });
89 | Object.defineProperty(this, 'response', { get: () => { return newText; } });
90 | });
91 | }
92 | return originalSend.apply(this, arguments);
93 | };
94 | }
95 |
96 | const _SafeTransactionHomeTaxPatcher = () => {
97 | if (location.host != "hometax.go.kr") return
98 | const originalOpen = XMLHttpRequest.prototype.open;
99 | const originalSend = XMLHttpRequest.prototype.send;
100 |
101 | XMLHttpRequest.prototype.open = function (method, url) {
102 | this._url = url;
103 | return originalOpen.apply(this, arguments);
104 | };
105 |
106 | XMLHttpRequest.prototype.send = function () {
107 | if (typeof this._url === 'string' && this._url.includes("js/comm/raonsecure/transkey/rsa_oaep_files/rsa_oaep-min.js")) {
108 | console.log("[SafeTransaction] Patching XMLHttpRequest for HomeTax RSA OAEP");
109 | this.addEventListener('readystatechange', e => {
110 | if (this.readyState != 4) return
111 | Object.defineProperty(this, 'statusText', { get: () => { return "Internal Server Error"; } });
112 | Object.defineProperty(this, 'status', { get: () => { return 500; } });
113 | });
114 | }
115 | return originalSend.apply(this, arguments);
116 | };
117 | }
118 |
119 | // _SafeTransactionNOSVKeypadPatchInitializer()
120 | _SafeTransactionKBankBizPatcher()
121 | _SafeTransactionHomeTaxPatcher()
122 | const _SafeTransactionOnWindowLoaded = () => {
123 | _SafeTransactionUserAgentPatcher()
124 | }
125 |
126 |
127 | if (document.readyState === "complete" || document.readyState === "interactive") {
128 | setTimeout(_SafeTransactionOnWindowLoaded, 1);
129 | } else {
130 | document.addEventListener("DOMContentLoaded", _SafeTransactionOnWindowLoaded);
131 | }
--------------------------------------------------------------------------------