Having a secure website is essential if you are using HTTP to connect to, say, a corporate database. Windows NT and 2000 websites can be secured using the following:
If an HTTP request fails authentication, an error 401 will be returned if the error originates in a web server, or 407 for a proxy server authentication failure. These errors are returned in HTTP headers from the server. If your application receives a 401 or 407 error, a valid username and password should be supplied.
The type of authentication can be configured in Microsoft Internet Information Server (IIS) for each website or virtual directory on a server. Further, an anonymous login can be specified, so that an unrecognized user can login using the specified login. This login name is usually based on the server name, for example, IUSR_MYSERVER, where MYSERVER is the name of the server IIS is installed on. With IIS, once the type of authentication has been configured, NTFS security is applied to the files in the website, and this controls who has what type of access to the files or directories.
The following two methods can be used to handle authentication errors:
Using the function InternetErrorDlg to prompt the user to supply a username and password
Using HttpQueryInfo to interrogate the HTTP headers returned from the request and calling InternetSetOption to set the username and password
You will need to specify the INTERNET_FLAG_KEEP_CONNECTION option when calling HttpOpenRequest so that the security options can be maintained between HTTP requests.
hHttpRequest = HttpOpenRequest(hHttpSession, NULL, // verb is 'GET' crackedURL.lpszUrlPath, NULL, // default version NULL, // no referrer NULL, // only accept text/* files INTERNET_FLAG_KEEP_CONNECTION, 0); // no context for call backs
One of the problems with using authentication with IIS is finding out which user is making the HTTP requests. Therefore, when testing your authentication code you should turn on logging, and look for the requests in the IIS logs. In the following examples, the first request was made with no username specified (-), and the second used "Administrator."
192.168.0.221, -, 28/02/00, 14:52:41, W3SVC1, SPL_WEB, 192.168.0.2, 71, 363, 761, 401, 5, GET, / site, -, 192.168.0.221, administrator, 28/02/00, 14:52:49, W3SVC1, SPL_WEB, 192.168.0.2, 1502, 414, 300, 302, 0, GET, /site/, -,
The function InternetErrorDlg can be used in a variety of ways to correct errors with HTTP requests. For example, the function can be used to prompt the user for a username and password in the event of an authentication error.
The code fragment in Listing 8.3 calls HttpSendRequest, and then calls InternetErrorDlg with the following options:
FLAGS_ERROR_UI_FILTER_FOR_ERRORS— Scans the returned headers for errors
FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS— Saves changes (such as the supplied username and password) in the HTTP headers associated with the hHttpRequest handle
FLAGS_ERROR_UI_FLAGS_GENERATE_DATA— Generates data to correct the errors, such as prompting the user for the username and password
InternetErrorDlg | |
---|---|
HWND hWnd | Window handle used as the parent for any dialogs that are displayed. |
HINTERNET hRequest | Request handle from HttpSendRequest. |
DWORD dwError | Error code for the problem to be rectified. |
DWORD dwFlags | Flags specifying type of action to take. |
LPVOID *lppvData | Data structure pointer specific for type of error being handled, or NULL for none. |
DWORD Return Value | ERROR_SUCCESS for success. |
ERROR_CANCELLED if user cancelled dialog box. | |
ERROR_INTERNET_FORCE_RETRY if HttpRequest should be resent. |
resend: if(!HttpSendRequest(hHttpRequest, NULL, 0, // no headers 0, 0)) // no optional data { cout ≪ _T("Could not send request ") ≪ GetLastError(); goto cleanUp; } DWORD dwErrorCode, dwError; dwErrorCode = hHttpRequest ? ERROR_SUCCESS : GetLastError(); dwError = InternetErrorDlg(GetFocus(), hHttpRequest, dwErrorCode, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL); if (dwError == ERROR_INTERNET_FORCE_RETRY) goto resend; // now read the data from the request // using InternetReadFile. |
Notice that when an authentication error is detected, the call to HttpSendRequeststill succeeds. If corrective action is not taken the Internet server will return an error message that will be read by InternetReadFile.
Authentication with InternetErrorDlg will display a dialog prompting for a username and password. In many situations, your application may already know the username and password to use and therefore should not prompt the user. In this situation, HttpQueryInfo is used to determine if the HTTP headers sent from the server contain authentication error information, and InternetSetOption sets the username and password for the request (Listing 8.4).
DWORD dwStatus, dwStatusSize; dwStatusSize = sizeof(DWORD); if(!HttpQueryInfo(hHttpRequest, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, &dwStatus, &dwStatusSize, NULL)) { cout ≪ _T("Could not query info") ≪ GetLastError(); } // Server Authentication Required if(dwStatus == HTTP_STATUS_DENIED) { // Set strUsername and strPassword InternetSetOption(hHttpRequest, INTERNET_OPTION_USERNAME, szUser, wcslen(szUser) + 1); InternetSetOption(hHttpRequest, INTERNET_OPTION_PASSWORD, szPassword, wcslen(szUser) + 1); } |
HttpQueryInfo is passed the following flags:
HTTP_QUERY_FLAG_NUMBER— Return the requested data as a DWORD
HTTP_QUERY_STATUS_CODE— Return the status (error) code associated with the request
A pointer to the DWORD dwStatus is passed, and this variable will contain the status number on return. Notice that dwStatusSize is initialized with the size of a DWORD. The variable dwStatus will contain the value HTTP_STATUS_DENIED (which is the value 401) if a server authentication error occurred.
HttpQueryInfo can be used to return all sorts of HTTP header information such as the length of the content to be returned (HTTP_QUERY_CONTENT_LENGTH) or its language (HTTP_QUERY_CONTENT_LANGUAGE), date when the content is deemed to have expired (HTTP_QUERY_EXPIRES), or the host and port number of the server (HTTP_QUERY_HOST).
HttpQueryInfo | |
---|---|
HINTERNET hRequest | Request handle to get headers for |
DWORD dwInfoLevel | Constant indicating what type of header information to obtain |
LPVOID lpBuffer | Pointer to a buffer in which data will be returned |
LPDWORD lpdwBufferLength | Length of lpBuffer on entry, number of bytes placed in lpBuffer on return |
LPDWORD lpdwIndex | Header index to return when dwInfoLevel may have more than one header |
BOOL Return Value | TRUE on success, FALSE for failure |
The function InternetSetOption is used to set the username and password into the HTTP headers for the request (Table 8.11).
InternetSetOption | |
---|---|
HINTERNET hInternet | Request handle |
DWORD dwOption | Constant indicating value to be set, e.g. INTERNET_OPTION_USERNAME |
LPVOID lpBuffer | Pointer to buffer containing value |
DWORD dwBufferLength | Number of bytes of data to set |
BOOL Return Value | TRUE on success, FALSE for failure |
The function can be used to set other options, such as the following:
The timeout value (INTERNET_OPTION_CONNECT_TIMEOUT)
The proxy name (INTERNET_OPTION_PROXY)
The user agent name (INTERNET_OPTION_USER_AGENT)
If you do not have access to a secure Internet site using NTLM, try connecting to http://www.softwarepaths.com/WinCEProgramming/Secure/Default.htm. This resource can be accessed with the user name "wince" and password "device."
3.144.230.82