| 검색 | ?

ASCII 표

제품화 관점에서 UI 및 CLI 구현시 입/출력 문자에 대한 가능 범위를 검토하게 되며 이 때 특정 data 형식으로 가공하는 경우 Escape 하게 될 문자를 정의하여 설계하는게 필요합니다.

본 페이지에서는 이러한 설계시 고려해야 하는 사항들을 열거합니다.

  • 예1) '\', '\\', '<', '>', '$', '#', '!', '?', '*', '`', '&', ';', ' ', '\t' 등을 UI에서 입력했더니 의도하지 않은 동작이 일어나는 것은 이러한 유효성 검토가 충분히 않다는 것.
  • 예2) UI 에서 SQL Injection 가능한 문자를 Escape 처리하지 않고 UI입력처리를 했다면? 보안문제를 인식하지 못했다는 것.
  • 예3) UTF-8 에서 써로게이트 문자를 입력했을 때 의도하지 않은 동작이 일어난다는 것은 Unicode 체계를 정확히 이해하지 못했다는 것.
  • 예4) "; reboot && reboot || reboot\nreboot" 이라는 문자(또는 유사한 Shell injection 문자열)를 입력하였더니 재부팅이 된다면 심각한 보안 문제가 있다는 것.

    ASCII NUL
    '\0'
    ^A
    (SOH)
    ^B
    (STX)
    ^C
    (ETX)
    ^D
    (EOT)
    ^E
    (ENQ)
    ^F
    (ACK)
    ^G
    (BEL)
    '\a'
    ^H
    (BS)
    '\b'
    ^L
    (HT)
    '\t'
    ^J
    (LF)
    '\n'
    ^K
    (VT)
    '\v'
    ^L
    (FF)
    '\f'
    ^M
    (CR)
    '\r'
    ^N
    (SO)
    ^O
    (SI)
    DEC 00 01 02 03 04 05 06 07 08 O9 10 11 12 13 14 15
    OCT 00 01 02 03 04 05 06 07 10 11 12 13 14 15 16 17
    HEX 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
    ASCII ^P
    (DLE)
    ^Q
    (DC1)
    ^R
    (DC2)
    ^S
    (DC3)
    ^T
    (DC4)
    ^U
    (NAK)
    ^V
    (SYN)
    ^W
    (ETB)
    ^X
    (CAN)
    ^Y
    (EM)
    ^Z
    (SUB)
    ^[
    (ESC)
    '\e'
    ^\
    (FS)
    ^]
    (GS)
    ^^
    (RS)
    ^_
    (US)
    DEC 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
    OCT 20 21 22 23 24 25 26 27 30 31 32 33 34 35 36 37
    HEX 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F
    ASCII SPACE ! " # $ % & ' ( ) * + , - . /
    DEC 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
    OCT 40 41 42 43 44 45 46 47 50 51 52 53 54 55 56 57
    HEX 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F
    ASCII 0 1 2 3 4 5 6 7 8 9 : ; < = > ?
    DEC 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
    OCT 60 61 62 63 64 65 66 67 70 71 72 73 74 75 76 77
    HEX 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F
    ASCII @ A B C D E F G H I J K L M N O
    DEC 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
    OCT 100 101 102 103 104 105 106 107 110 111 112 113 114 115 116 117
    HEX 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F
    ASCII P Q R S T U V W X Y Z [ \ ] ^ _
    DEC 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    OCT 120 121 122 123 124 125 126 127 130 131 132 133 134 135 136 137
    HEX 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F
    ASCII ` a b c d e f g h i j k l m n o
    DEC 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
    OCT 140 141 142 143 144 145 146 147 150 151 152 153 154 155 156 157
    HEX 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F
    ASCII p q r s t u v w x y z { | } ~ ^?
    (DEL)
    DEC 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
    OCT 160 161 162 163 164 165 166 167 170 171 172 173 174 175 176 177
    HEX 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F


  • ctypes table code by 'C'
    More
  • UNICODE 에서 ASCII 문자 대역 범위
    • 0 ~ 0x7F
  • BASE64 encoding 문자 허용 범위
    • BASE64 표준 문자 범위
      ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
      
    • BASE64 URL AND FILENAME SAFE 표준 문자 범위
      ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=
      
    • BASE64 WRAP 기능을 허용하는 경우 LF (0x0a, '\n'), CR (0x0d, '\r') 허용하되 무시
  • DIGIT : 10진수 숫자의 범위
    0123456789
    
  • XDIGIT : 16진수 숫자의 범위
    abcdefABCDEF0123456789
    
    • DIGIT 포함
  • BLANK SPACE 계열의 문자 범위 : 수직적 공간의 공백 문자
    HT (0x09, '\t')
    SP (0x20, ' ')
    
  • WHITE SPACE 계열의 문자 범위 : 모든 공백 문자
    HT (0x09, '\t')
    LF (0x0a, '\n')
    VT (0x0b)
    FF (0x0c)
    CR (0x0d, '\r')
    SP (0x20, ' ')
    
    • BLANK SPACE 포함
  • PUNCH 계열 문자 범위 (checks for any printable character which is not a space or an alphanumeric character)
    !"#$%&'()*+,-./<=>?@{|}~
    
    • ASCII 대역 중에서 PRINT 계열 중 ALPHABET 과 WHITE SPACE를 제외한 문자들
  • GRAPH 계열 문자 범위 : 콘솔/화면등에서 눈에 보이는 문자들
    • PUNCH 계열 포함
    • ALPHABET 대소문자 포함
    • DIGIT 포함
  • PRINT 계열 문자 범위 : 콘솔/화면/인쇄 등에서 실제 공간을 차지하는 영역을 가진 인쇄에서 인지하는 처리 문자들
    • GRAPH 계열 포함
    • WHITE SPACE 계열 포함
  • PRINTUSASCII 문자 범위
  • FQDN (Fully-qualified domain name) 문자열 규약에 맞는 형식
    • 각 계층별 0~63자 (계층은 '.'으로 구분, 맨마지막 '.'은 root를 의미하지만 통상적으로 생략, 각 계층을 label이라고 함.)
    • 전체 도메인 최대 255자
    • 허용문자 (label 허용문자 규칙) : 알파벳 (대/소문자 구분 없이 사용), 숫자(0~9), 대시(-) 만 허용
      • 하이픈으로 시작하거나 끝날 수는 없다
      • 마이크로소프트 윈도 등의 시스템에서는 이를 무시하고 밑줄 문자를 흔히 사용하며, 호스트 이름으로 인식되지 않게 하기 위해서 의도적으로 밑줄로 시작하는 이름을 쓰기도 한다. (SRV 레코드 등)
        • 밑줄 ('_') 문자를 사용하는 경우 호스트이름으로는 허용하지 않지만 SRV 레코드와 관련하여 ([https]RFC2782 - A DNS RR for specifying the location of services (DNS SRV)[]) 밑줄을 사용하기도 함. (엄밀히 이 경우는 호스트명이 주어질 수 없으므로 FQDN이라고 부르기는 어렵다고 해석됨.)
    • 도메인 계층 구분
      • root = '.'
      • TLD (Top-Level Domain, 1단계 도메인, 최상위 도메인) = 'com', 'net', 'org', 'edu', 'gov', 'mil', 'int', 'kr', 'jp, ...
      • Branch (2단계 도메인, TLD 아래의 도메인) = 'my.com', 'example.com', ...
      • Leaf (마지막 계층의 도메인, 하위 계층이 없는) = 'myhost.my.com', 'test.example.com', ...
    • 예: "myvpn.example.com"
  • User FQDN (User Fully-qualified domain name)
    • 기본 구성 : <local-part>@<domain-part(FQDN)>
    • local-part (최대 64자)
      • backslash ('\')는 다음에 오는 문자를 그 자체로 표현하려할 때 사용. (단, local-part 가 quote ('"') 문자로 쌓여있는 않는 경우에 사용함)
        • 예) Abc\@def@example.com
        • 예) Fred\ Bloggs@example.com
        • 예) Joe.\\Blow@example.com
        • 예) "Abc@def"@example.com
        • 예) "Fred Bloggs"@example.com
      • quote ('"')로 쌓여있지 않는 경우에는 기본적으로 알파벳, 숫자, 그리고 아래 명시한 특수 문자만 허용됩니다. (그 밖에는 quote ('"') 로 감싸거나 backslash ('\') 로 표현해야 합니다.)
        ! # $ % & ' * + - / = ?  ^ _ ` . { | } ~
        
        • 예) user+mailbox@example.com
        • 예) customer/department=shipping@example.com
        • 예) $A12345@example.com
        • 예) !def!xyz%abc@example.com
        • 예) _somename@example.com
      • apostrophe ("'") 와 acute accent ("`") 는 quote ('"') 와 같은 의미가 아님에 유의.
    • 총 길이는 local-part 64자와 '@'문자 1자 그리고 domain-part 255자로 구성되어 최대 320자를 넘지 않아야 합니다.
    • 예: "jsmith@myvpn.example.com"
  • IP 표준화 문자열
    • IPv4 유효성 범위
      • '.'으로 구분되는 4개의 숫자 (0~255), '.' 사이에 숫자가 생략될 수 없음.
    • IPv6 유효성 범위
      • ':'으로 구분되는 16진수 (16bits octet) 숫자 (0~ffff)
      • '::' 의 경우 미지정 (Unspecified) 주소라고 함.
      • x:x:x:x:x:x:x:x형태로 표현되며 x는 16비트의 16진수로 표현합니다. 대소문자를 구분하지 않으며 각 항목의 상위 숫자 0은 생략해서 표기해도 되지만 각 항목에는 적어도 하나의 숫자가 있어야 합니다.
        • 예1) fe80:0000:0000:0000:0213:d4ff:fe47:14d5
        • 예2) fe80:0:0:0:213:d4ff:fe47:14d5
      • 일반적으로 0비트가 많이 포함되므로 이를 간략히 표기할 방법을 나타내기 위해서 16비트 0의 연속된 항목을 "::" 기호로 대체할수 있습니다. 단, 이러한 축약기호는 단 한번만 사용할수 있습니다.
        • 예1) fe80:0000:0000:0000:0213:d4ff:fe47:14d5 => fe80::213:d4ff:fe47:14d5
        • 예2) 0:0:0:0:0:0:0:1 => ::1
        • 예3) 0:0:0:0:0:0:0:0 => ::
      • IPv4주소를 (포함하는) IPv6주소로 나타내는 경우 x:x:x:x:x:x:d.d.d.d 로 나타낼수 있습니다. 여기서 x는 16비트 16진수이며 d는 8비트 10진수로 표현합니다.
        • 예1) (IPv4 compatible 주소) 0:0:0:0:0:0:192.168.0.1 => ::192.168.0.1
        • 예2) (IPv4 mapped 주소) 0:0:0:0:0:ffff:192.168.0.2 => ::ffff:192.168.0.2
      • 범주지정 주소 표현하는 방법 : 주소만으로는 어떤 범주인지를 명시하지 않았을 경우 오는 모호성을 해결할수 없는 경우가 있습니다. 이때 주소%영역 과 같이 표시하여 모호성을 해결합니다.
        • Windows 에서 "ipconfig /all" 명령으로 확인시 이러한 주소가 표기되는 것을 볼 수 있음.
        • 예1) fe80::213:d4ff:fe47:14d5%eth0
    • CIDR : <ip>/<prefix>
    • NETMASK : <ip>/<netmask>
      • Subnet Mask Cheat Sheet
    • RANGE : <ip1>-<ip2> OR <ip1>~<ip2>
    • LIST : <SINGLE | CIDR | NETMASK | RANGE>,[...]
    • python 기준 IPv4/IPv6 주소 식별 표준 함수
      • socket.inet_pton({socket.AF_INET or socket.AF_INET6}, {str})
        • 주어진 문자열을 in_addr 또는 in6_addr 형태의 Network order binary (bytearray string) 로 변환한다.
        • 문자열이 주어진 address family 에 허용하지 않는 문자열인 경우 예외 (exception) 가 발생된다.
        • 사용 예) 주어진 IP 주소 문자열을 inX_addr 형태 및 이를 이용한 정수화 예시
          >>> import struct
          >>> import socket
          >>> in6_addr = socket.inet_pton(socket.AF_INET6, u"1234:5678:abcd:ffff::9999")
          >>> in6_addr
          '\x124Vx\xab\xcd\xff\xff\x00\x00\x00\x00\x00\x00\x99\x99'
          >>> a6_16 = struct.unpack(u"HHHHHHHH", socket.inet_pton(socket.AF_INET6, u"1234:5678:abcd:ffff::9999"))
          >>> a6_16
          (13330, 30806, 52651, 65535, 0, 0, 0, 39321)
          >>> "%x:%x:%x:%x:%x:%x:%x:%x"%(socket.ntohs(a6_16[0]),socket.ntohs(a6_16[1]),socket.ntohs(a6_16[2]),socket.ntohs(a6_16[3]),socket.ntohs(a6_16[4]),socket.ntohs(a6_16[5]),socket.ntohs(a6_16[6]),socket.ntohs(a6_16[7]))
          '1234:5678:abcd:ffff:0:0:0:9999'
          >>> a6_32 = struct.unpack(u"IIII", socket.inet_pton(socket.AF_INET6, u"1234:5678:abcd:ffff::9999"))
          >>> a6_32
          (2018915346, 4294954411, 0, 2576941056)
          >>> in_addr = socket.inet_pton(socket.AF_INET, u"101.102.103.104")
          in_addr = socket.inet_pton(socket.AF_INET, u"101.102.103.104")
          >>> in_addr
          'efgh'
          >>> a4_8 = struct.unpack(u"BBBB", socket.inet_pton(socket.AF_INET, u"101.102.103.104"))
          >>> a4_8
          (101, 102, 103, 104)
          >>> a4_32 = struct.unpack(u"I", socket.inet_pton(socket.AF_INET, u"101.102.103.104"))
          >>> a4_32
          (1751606885,)
          >>> "%08x"%socket.ntohl(a4_32[0])
          '65666768'
          
      • socket.inet_ntop({socket.AF_INET or socket.AF_INET6}, {inX_addr})
        • 주어진 inX_addr Network order binary (bytearray string) 를 string 형태로 변환한다.
        • 주어진 inX_addr 크기가 address family 와 맞지 않는 경우 예외 (exception) 가 발생된다.
        • 사용 예) prefix 64 를 IPv6 netmask 로 출력해주는 예시
          >>> import socket
          >>> s_address_family = socket.AF_INET6
          >>> s_max_prefix_length = 128
          >>> s_prefix_length = 64
          >>>
          >>> s_bytearray = list()
          >>> s_prefix_index = 0
          >>> while s_prefix_index < s_max_prefix_length:
          ...     s_this_value = 0
          ...     for s_bit_index in xrange(8):
          ...         if s_prefix_index < s_prefix_length:
          ...             s_this_value |= 1 << (7 - s_bit_index)
          ...         s_prefix_index += 1
          ...     s_bytearray.append(s_this_value)
          ...
          >>> s_inX_addr = str(bytearray(s_bytearray))
          >>>
          >>> str = socket.inet_ntop(s_address_family, s_inX_addr)
          >>> str
          'ffff:ffff:ffff:ffff::'
          
        • 사용 예) 바이트 단위로 정수 값을 배열화하여 문자열 IPv4/IPv6 주소로 생성하는 예시
          >>> import socket
          >>> s_bytearray = list()
          >>> s_bytearray.append(101)
          >>> s_bytearray.append(102)
          >>> s_bytearray.append(103)
          >>> s_bytearray.append(104)
          >>> in_addr = str(bytearray(s_bytearray))
          >>> in_addr
          'efgh'
          >>> str = socket.inet_ntop(socket.AF_INET, in_addr)
          >>> str
          '101.102.103.104'
          >>> import struct
          >>> in_addr = struct.pack(u"I", socket.htonl(0x01020304))
          >>> str = socket.inet_ntop(socket.AF_INET, in_addr)
          >>> str
          '1.2.3.4'
          >>> in6_addr = struct.pack(u"HHHHHHHH", socket.htons(0x1234), socket.htons(0x2345), socket.htons(0x6789), socket.htons(0x0ABC), 0, 0, 0, socket.htons(0x9999))
          >>> str = socket.inet_ntop(socket.AF_INET6, in6_addr)
          >>> str
          '1234:2345:6789:abc::9999'
          
      • 사용 예) 어떤 주소 표현이던지 유효 (사람이 인식 가능한 수준이라면) 하다면 IPv4/IPv6 주소 문자열을 축약형 문자열로 변환해주는 함수 구현 예 => self._normalize_address("<IPv4 또는 IPv6>") 형태로 사용
        import socket
        
        # IP 주소의 경우 다른 문자열이 같은 주소인 경우가 있으므로 이에 대한 비교 수행시 간략형 주소로 변경하여 비교하도록 고려해야 함.
        # => s_address_family 가 socket.AF_UNSPEC 으로 호출되는 경우 s_value가 None이 아닌 이상 절대로 반환값은 None이 반환되지 않습니다.
        #    이 경우 s_value 가 IPv4/IPv6인 경우는 축약형(normalize)으로 반환되며 나머지의 경우는 그대로 반환됩니다.
        # => s_address_family 가 socket.AF_INET 으로 호출되는 경우 s_value가 IPv4주소가 아닌 경우는 None이 반환됩니다.
        # => s_address_family 가 socket.AF_INET6 으로 호출되는 경우 s_value가 IPv6주소가 아닌 경우는 None이 반환됩니다.
        def _normalize_address(self, s_value, s_default_value = None, s_address_family = socket.AF_UNSPEC):
            if s_value == None:
                return s_default_value
        
            s_normalize_address = None
        
            # s_value의 IPv4, IPv6, FQDN 을 자동으로 감지하여 적절히 normalize 합니다. (재귀 호출로 구현)
            if s_address_family == socket.AF_UNSPEC:
                for s_this_address_family in [socket.AF_INET, socket.AF_INET6, None]:
                    s_normalize_address = _normalize_address(self, s_value, s_default_value, s_this_address_family)
                    if s_normalize_address != None:
                        break
                return s_normalize_address
        
            # IPv4 주소에 한하여 normalize 합니다. 그 외의 경우는 None을 반환합니다.
            if s_address_family == socket.AF_INET:
                try:
                    # 표준 normalize
                    s_inX_addr = socket.inet_pton(s_address_family, s_value)
                    s_normalize_address = socket.inet_ntop(s_address_family, s_inX_addr)
                except:
                    # IPv4 주소중에는 1.2.3.4 가 정상이지만 001.002.003.004 형태로 입력하는 경우도 있을 수는 있다. (여기서는 이를 1.2.3.4로 변환)
                    s_ipv4_pieces = s_value.split('.')
                    if len(s_ipv4_pieces) == 4:
                        s_temp_string = ''
                        for s_piece in s_ipv4_pieces:
                            try:
                                if int(s_piece) < 0 and int(s_piece) >= 256:
                                    break
                                if s_temp_string:
                                    s_temp_string += '.' + str(int(s_piece))
                                else:
                                    s_temp_string += str(int(s_piece))
                            except:
                                # 숫자가 아님
                                break
                        else:
                            s_normalize_address = s_temp_string
                return s_normalize_address
        
            # IPv6 주소에 한하여 normalize 합니다. 그 외의 경우는 None을 반환합니다.
            if s_address_family == socket.AF_INET6:
                try:
                    # 표준 normalize
                    s_inX_addr = socket.inet_pton(s_address_family, s_value)
                    s_normalize_address = socket.inet_ntop(s_address_family, s_inX_addr)
                except:
                    # TODO : IPv6 compat/mapping 주소에서 IPv4 부분의 001.002.003.004 같은 형식의 입력에 대한 고려는 여기서 필요
                    s_normalize_address = None
                return s_normalize_address
        
            # FQDN / Interface-name (이 경우 그냥 반환합니다. 대소문자 고려는 호출부에서 하도록 합니다)
            return s_value
        

참고사항



Copyright ⓒ MINZKN.COM
All Rights Reserved.