[Python] 신뢰할 수 있는 접속URL을 자동으로 등록해 주는 Python 코드

브라우저에 등록된 Experiments 중 “Insecure origins treated as secure(unsafely-treat-insecure-origin-as-secure)”에 신뢰할 수 있는 접속URL을 자동으로 등록해 주는 Python 코드입니다. pyinstaller 패키지를 설치한 후 py -m PyInstaller -F .\register_secure_url.py를 실행하여 파이션 실행파일을 생성해서 배포하면 편리하게 신뢰할 수 있는 URL을 자동 등록할 수 있습니다.

파일명: register_secure_url.py
import os
import json
from pathlib import Path
import subprocess
import time
import re
import datetime
import sys

def terminate_browser(browser_name: str):
    """
    지정된 브라우저 프로세스를 강제 종료합니다.
    """
    browser_name = browser_name.lower()
    
    if browser_name == 'chrome':
        process_name = 'chrome.exe'
    elif browser_name == 'edge':
        process_name = 'msedge.exe'
    else:
        print(f"에러: 알 수 없는 브라우저 '{browser_name}'")
        return False

    command = f'taskkill /f /im {process_name}'
    
    try:
        # shell=True는 Windows 명령어를 실행할 수 있게 해줍니다.
        # check=True는 명령어 실행 실패 시 예외를 발생시킵니다.
        subprocess.run(command, shell=True, check=True, 
                       stdout=subprocess.PIPE, stderr=subprocess.PIPE, 
                       encoding='cp949') # Windows에서 한글 출력을 위해 cp949 인코딩 사용
        
        print(f"{browser_name} 프로세스를 성공적으로 강제 종료했습니다.")
        time.sleep(1) # 프로세스가 완전히 종료될 시간을 잠시 대기
        return True
        
    except subprocess.CalledProcessError as e:
        # 프로세스가 실행 중이 아니었을 경우 (오류가 아닐 수 있음)
        if re.search(r".*프로세스.*찾을 수 없습니다", e.stderr.strip()):
             print(f"{browser_name} 프로세스는 실행 중이지 않습니다.")
             return True # 종료할 프로세스가 없었으므로 성공으로 간주
        else:
             print(f"{browser_name} 종료 실패: {e.stderr.strip()}")
             return False
    except Exception as e:
        print(f"예상치 못한 오류: {e}")
        return False


def register_secure_url(browser_name: str, new_url: str):
    """
    Chrome 또는 Edge의 Local State 파일에서 지정된 실험 URL 값을 읽고 씁니다.
    """
    if not terminate_browser(browser_name):
       return False
     
    # 1. 파일 경로 설정
    appdata_local_path = Path(os.getenv('LOCALAPPDATA'))
    
    if browser_name.lower() == 'chrome':
        file_path = appdata_local_path / "Google" / "Chrome" / "User Data" / "Local State"
    elif browser_name.lower() == 'edge':
        file_path = appdata_local_path / "Microsoft" / "Edge" / "User Data" / "Local State"
    else:
        print("에러: 'chrome' 또는 'edge'를 지정해주세요.")
        return False

    # 2. 파일 존재 확인
    if not file_path.exists():
        print(f"에러: {browser_name}의 Local State 파일이 존재하지 않습니다. 경로: {file_path}")
        return False

    # 3. JSON 파일 읽기 및 쓰기
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            data = json.load(f)

        key_browser = "browser"
        key_enable_labs = "enabled_labs_experiments"
        key_origin_lists = "enabled_labs_experiments_origin_lists"
        key_as_secure = "unsafely-treat-insecure-origin-as-secure"

        # JSON 딕셔너리 계층 구조에 따라 값에 접근합니다.
        # data["키1"]["키2"]["찾는_키"]
        old_url = data.get(key_browser, {}) \
                      .get(key_origin_lists, {}) \
                      .get(key_as_secure)

        # 1단계 키: browser
        if key_browser not in data:
            data[key_browser] = {} # 키가 없으면 빈 딕셔너리로 생성
        
        # 2단계 키: enabled_labs_experiments 
        # enabled_labs_experiments항목에 unsafely-treat-insecure-origikey_as_securen-as-secure가 등록되어 있어야 함
        if key_enable_labs not in data[key_browser]:
            data[key_browser][key_enable_labs] = [] # 키가 없으면 빈 리스트로 생성

        labs_list = data[key_browser].get(key_enable_labs, [])
        if key_as_secure not in labs_list:
            labs_list.append(key_as_secure)
        data[key_browser][key_enable_labs] = labs_list

        # 2단계 키: enabled_labs_experiments_origin_lists        
        if key_origin_lists not in data[key_browser]:
            data[key_browser][key_origin_lists] = {} # 키가 없으면 빈 딕셔너리로 생성

        # 3단계 키: unsafely-treat-insecure-origin-as-secure
        # MES 접속URL 등록
        data[key_browser][key_origin_lists][key_as_secure] = new_url 

        # Local State 파일은 구조가 매우 길기 때문에, 파일 크기가 변경되면 안 됩니다.
        # 기존 파일을 덮어쓰기 위해 'w' 모드를 사용합니다.
        with open(file_path, 'w', encoding='utf-8') as f:
            # indent=None, separators=(',', ':') : 원본 Local State 파일은 가독성을 위한
            # 들여쓰기 없이 한 줄로 저장되는 경우가 많으므로, 이를 유지하는 것이 안전합니다.
            json.dump(data, f, indent=None, separators=(',', ':'))

        # print(f"{browser_name}에서 읽은 'unsafely-treat-insecure-origin-as-secure' 값:")
        # print(f"Old URL - {old_url}")
        # print(f"New URL - {new_url}")
        return True

    except json.JSONDecodeError:
        print(f"❌ 에러: {browser_name} Local State 파일의 JSON 형식이 올바르지 않습니다.")
        return False
    except Exception as e:
        print(f"❌ 예상치 못한 오류 발생: {e}")
        return False


def time_to_chromium_format(gubun: str):
    """
    현재 UTC 시간을 Chromium/Chrome이 사용하는 시간 형식(1601년 1월 1일 
    UTC 자정부터 경과한 마이크로초)으로 변환하여 반환합니다.
    """
    # 1. 1601년 1월 1일 UTC를 기준으로 하는 datetime 객체 정의
    CHROMIUM_EPOCH = datetime.datetime(1601, 1, 1, tzinfo=datetime.timezone.utc)
    
    # 2. 현재 UTC 시간
    # time.time()은 1970년 1월 1일 UTC부터의 초를 반환합니다.
    # datetime.datetime.now(datetime.timezone.utc)를 사용해도 되지만, 
    # time.time()을 사용하면 부동 소수점 오차를 최소화할 수 있습니다.
    if gubun == 'added':
        utc_time = datetime.datetime.fromtimestamp(time.time(), tz=datetime.timezone.utc)
    elif gubun == 'expired':
        utc_time = datetime.datetime(2099, 12, 31, 23, 59, 59, tzinfo=datetime.timezone.utc)
    else:
        utc_time = datetime.datetime(2025, 11, 16, 23, 59, 59, tzinfo=datetime.timezone.utc)
    
    # 3. 1601-01-01 UTC 와 현재 UTC 시간의 차이 계산
    time_difference = utc_time - CHROMIUM_EPOCH
    
    # 4. 차이를 총 마이크로초로 변환 (time.timedelta.total_seconds()를 사용)
    # total_seconds()는 소수점 이하까지 정확한 초를 반환합니다.
    total_microseconds = int(time_difference.total_seconds() * 1_000_000)
    
    return str(total_microseconds)

def register_ie_mode_page(new_url: str):
    """
    Internet Explorer 모드 페이지 등록(설정 > 기본 브라우저 > Internet Explorer 호환성)
    """
    if not terminate_browser("edge"):
       return False

    # 1. 파일 경로 설정
    appdata_local_path = Path(os.getenv('LOCALAPPDATA'))
    file_path = appdata_local_path / "Microsoft" / "Edge" / "User Data" / "Default" / "Preferences"

    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        key_level1 = "dual_engine"
        key_level2 = "user_list_data_1"
        key_level3_urls = new_url.split(",")
        
        ie_mode_pages = data.get(key_level1, {}) \
                            .get(key_level2, {})

        for temp in key_level3_urls:
            key_level3 = temp.strip()
            if key_level3 not in ie_mode_pages:
                data[key_level1][key_level2][key_level3] = {}
            else:
                data[key_level1][key_level2][key_level3].clear()
            
            data[key_level1][key_level2][key_level3]["date_added"] = time_to_chromium_format('added')
            data[key_level1][key_level2][key_level3]["engine"] = 2
            data[key_level1][key_level2][key_level3]["source"] = 3
            data[key_level1][key_level2][key_level3]["visit_state"] = False
            data[key_level1][key_level2][key_level3]["visits_after_expiration"] = 0
            # data[key_level1][key_level2][key_level3]["expiration_date"] = time_to_chromium_format('expired')
            # data[key_level1][key_level2][key_level3]["date_expired"] = time_to_chromium_format('expired')

        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(data, f, indent=None, separators=(',', ':'))

        # print(f"현재 등록된 IE 모드 페이지: {data.get(key_level1, {}).get(key_level2, {}).keys()}")

        return True
    
    except json.JSONDecodeError:
        print(f"❌ 에러: Preferences 파일의 JSON 형식이 올바르지 않습니다.")
        return False
    except Exception as e:
        print(f"❌ 예상치 못한 오류 발생: {e}")
        return False


def confirm_continuation_in_cmd():
    # CMD 프롬프트에서 사용자에게 'Y/N' 입력을 받아 계속 진행 여부를 확인합니다.    
    # 1. 경고 메시지 출력
    print("\n" + "*"*50)
    print(" Registering for Secure URLs and IE Mode Pages")
    print("       (Created by KVM IT on 2025.12.18)")
    print("*"*50)
    print("               ⚠️  경고: 작업 확인⚠️")
    print("계속 진행하면 모든 브라우저 세션이 강제로 닫힙니다.")
    
    # 2. 사용자 입력 루프
    while True:
        # 사용자로부터 입력 받기
        # .strip().lower()를 사용하여 입력의 앞뒤 공백을 제거하고 소문자로 변환
        response = input("계속 진행하시겠습니까? (Y/N)(Yes/No)(예/아니오): ").strip().lower()
        
        # '예'를 선택한 경우
        if response in ['y', 'yes', '예']:
            print("\n✔️ 사용자 확인: 작업을 계속 진행합니다.")
            # 여기에 실제 브라우저를 닫고 진행할 코드를 추가합니다.
            break # while 루프를 빠져나가 함수를 종료 (진행)
            
        # '아니오'를 선택한 경우
        elif response in ['n', 'no', '아니오']:
            print("\n❌ 사용자 취소: 작업을 종료합니다. (브라우저를 닫지 않습니다.)")
            sys.exit(0) # 프로그램 강제 종료
            
        # 유효하지 않은 입력인 경우
        else:
            print("→ 잘못된 입력입니다. 'Y/Yes/예' 또는 'N/No/아니오'을 입력해 주세요.")


if __name__ == "__main__":
    confirm_continuation_in_cmd()

    new_url = 'http://58.54.160.110:1134/,http://58.54.160.110:1136/,http://58.54.160.110:1137/,http://58.54.160.110:1152/,'
    new_url += 'http://58.54.160.111:1134/,http://58.54.160.111:1136/,http://58.54.160.111:1137/,http://58.54.160.111:1152/'

    # 1. Chrome 파일에서 값 읽기 시도
    print("\n--------------------------\n")
    if register_secure_url('chrome', new_url):
        print(f"Chrome 브라우저의 Secure URLs 등록 작업이 성공하였습니다.")

    # 2. Edge 파일에서 값 읽기 시도
    print("\n--------------------------\n")
    if register_secure_url('edge', new_url):
        print(f"Edge 브라우저의 Secure URLs 등록 작업이 성공하였습니다.")

    print("\n--------------------------\n")
    # 3. Edge에 IE 모드 페이지 등록
    if register_ie_mode_page('http://58.54.160.110:1134/'):
        print(f"Edge 브라우저에서 IE Mode Pages 등록 작업이 성공하였습니다.")

You may also like...

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다