@@ -2,10 +2,12 @@ package parser
22
33import (
44 "context"
5+ "errors"
56 "net"
67 "net/http"
78 "net/url"
89 "strings"
10+ "syscall"
911 "time"
1012
1113 "github.com/dyatlov/go-opengraph/opengraph"
@@ -60,82 +62,36 @@ func isPrivateIP(ip net.IP) bool {
6062 return false
6163}
6264
63- // validateURL はURLがSSRFに対して安全かどうかを検証し、検証済みのIPアドレスを返します
64- func validateURL (u * url.URL ) ([]net.IP , error ) {
65- // スキームの検証
66- if u .Scheme != "http" && u .Scheme != "https" {
67- return nil , ErrNotAllowed
68- }
69-
70- // ホスト名を取得
71- host := u .Hostname ()
72- if host == "" {
73- return nil , ErrNotAllowed
74- }
75-
76- // IPアドレスの場合は直接検証
77- if ip := net .ParseIP (host ); ip != nil {
78- if isPrivateIP (ip ) {
79- return nil , ErrNotAllowed
80- }
81- return []net.IP {ip }, nil
82- }
83-
84- // ホスト名の場合はDNS解決して検証
85- ips , err := net .LookupIP (host )
86- if err != nil {
87- return nil , ErrNetwork
88- }
89-
90- for _ , ip := range ips {
91- if isPrivateIP (ip ) {
92- return nil , ErrNotAllowed
93- }
94- }
95-
96- return ips , nil
97- }
98-
9965// ParseMetaForURL 指定したURLのメタタグをパースした結果を返します。
10066func ParseMetaForURL (url * url.URL ) (* opengraph.OpenGraph , * DefaultPageMeta , error ) {
10167 _ = requestLimiter .Acquire (context .Background (), 1 )
10268 defer requestLimiter .Release (1 )
10369
104- // SSRF対策: URLを検証し、検証済みIPアドレスを取得
105- resolvedIPs , err := validateURL (url )
106- if err != nil {
107- return nil , nil , err
108- }
109-
11070 og , meta , isSpecialDomain , err := FetchSpecialDomainInfo (url )
11171 if isSpecialDomain && (err == nil ) {
11272 return og , meta , nil
11373 }
11474
115- // SSRF対策: 検証済みのIPアドレスを使用してリクエストを送信
75+ // SSRF対策: DNS解決後のIPアドレスを検証してプライベートIPへのアクセスをブロック
11676 dialer := & net.Dialer {
11777 Timeout : 5 * time .Second ,
118- }
119- transport := & http.Transport {
120- DialContext : func (ctx context.Context , network , addr string ) (net.Conn , error ) {
121- _ , port , err := net .SplitHostPort (addr )
78+ Control : func (_ , address string , _ syscall.RawConn ) error {
79+ host , _ , err := net .SplitHostPort (address )
12280 if err != nil {
123- return nil , err
81+ return err
12482 }
125-
126- // 検証済みのIPアドレスに接続
127- addr = net .JoinHostPort (resolvedIPs [0 ].String (), port )
128- return dialer .DialContext (ctx , network , addr )
83+ ip := net .ParseIP (host )
84+ if isPrivateIP (ip ) {
85+ logger .Info ("blocked request to private IP" , zap .String ("url" , url .String ()), zap .String ("ip" , host ))
86+ return errors .New ("private IP address is not allowed" )
87+ }
88+ return nil
12989 },
13090 }
131-
13291 client := http.Client {
133- Timeout : 5 * time .Second ,
134- Transport : transport ,
135- CheckRedirect : func (req * http.Request , _ []* http.Request ) error {
136- // Validate redirect destination to prevent SSRF via redirects
137- _ , err := validateURL (req .URL )
138- return err
92+ Timeout : 5 * time .Second ,
93+ Transport : & http.Transport {
94+ DialContext : dialer .DialContext ,
13995 },
14096 }
14197
0 commit comments