てきとうなメモ

本の感想とか技術メモとか

proxy認証のユーザ名とパスワードに使う文字

proxy認証する場合にhttp_proxy変数に

$ export http_proxy=http://user:pass@host:port

とやるのだが、userやpassに':'や'@'を入れたい場合はどうするのかなと思ってちょっと調べてみた。ユーザ名には使わないだろうけどパスワードだとありうるので。

おそらく、アプリケーション依存だと思うのだけども、GNU WgetcURLの場合はパーセントエンコードをすれば良いようである。

Wgetの方はsrc/url.cのparse_credentials関数で、urlからuserとpasswdを抽出する時にunescapeしている。

static bool 
parse_credentials (const char *beg, const char *end, char **user, char **passwd)
{
  char *colon;
  const char *userend;

  if (beg == end) 
    return false;               /* empty user name */

  colon = memchr (beg, ':', end - beg);
  if (colon == beg) 
    return false;               /* again empty user name */

  if (colon)
    {    
      *passwd = strdupdelim (colon + 1, end);
      userend = colon;
      url_unescape (*passwd);
    }    
  else 
    {    
      *passwd = NULL;
      userend = end; 
    }    
  *user = strdupdelim (beg, userend);
  url_unescape (*user);
  return true;
}

cURLの方はlib/url.cでproxyのURLをparseするparse_proxy関数の中でunescapeを行っている

static CURLcode parse_proxy(struct SessionHandle *data,
                            struct connectdata *conn, char *proxy)
{
...
    if(1 <= sscanf(proxyptr,
                   "%" MAX_CURL_USER_LENGTH_TXT"[^:@]:"
                   "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
                   proxyuser, proxypasswd)) {
      CURLcode res = CURLE_OK;

      /* found user and password, rip them out.  note that we are
         unescaping them, as there is otherwise no way to have a
         username or password with reserved characters like ':' in
         them. */
      Curl_safefree(conn->proxyuser);
      conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);

      if(!conn->proxyuser)
        res = CURLE_OUT_OF_MEMORY;
      else {
        Curl_safefree(conn->proxypasswd);
        conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);

        if(!conn->proxypasswd)
          res = CURLE_OUT_OF_MEMORY;
      }
...
}

仕様としてはURIRFCとしてRFC 3986があるのだけども、特にどうするとは明記していないように思える。URIにユーザ情報を入れることができるとは書いてあるが、

3.2.  Authority

   Many URI schemes include a hierarchical element for a naming
   authority so that governance of the name space defined by the
   remainder of the URI is delegated to that authority (which may, in
   turn, delegate it further).  The generic syntax provides a common
   means for distinguishing an authority based on a registered name or
   server address, along with optional port and user information.

   ...

      authority   = [ userinfo "@" ] host [ ":" port ]

パスワードをそのまま入れることに関してはセキュリティの面からdeprecatedになっている。ただ、proxy認証をするときの設定に利用することを意識しているのか不明であるし、パーセントエンコードの文字列(pct-encoded)をuserinfoの部分に書くのは認められているので、上記のような実装が妥当なのだろう。

3.2.1.  User Information

   The userinfo subcomponent may consist of a user name and, optionally,
   scheme-specific information about how to gain authorization to access
   the resource.  The user information, if present, is followed by a
   commercial at-sign ("@") that delimits it from the host.

      userinfo    = *( unreserved / pct-encoded / sub-delims / ":" )

   Use of the format "user:password" in the userinfo field is
   deprecated.  Applications should not render as clear text any data
   after the first colon (":") character found within a userinfo
   subcomponent unless the data after the colon is the empty string
   (indicating no password).  Applications may choose to ignore or
   reject such data when it is received as part of a reference and
   should reject the storage of such data in unencrypted form.  The
   passing of authentication information in clear text has proven to be
   a security risk in almost every case where it has been used.