HTTP client error 403 - delphi

I have the following problem: when I click a button on a site (http://domain-location.com) I receive information back to me and the url is changed to http://domain-location.com?key=value.
I want to create a sample http client to receive information easily. Here is my code:
function DoRequest(aVal: string): string;
const DOMAIN = 'http://domain-location.com';
var
request: TIdHTTP;
responseStream: TMemoryStream;
responseLoader: TStringList;
urlRequest: string;
begin
request := TIdHTTP.Create(nil);
responseStream := TMemoryStream.Create;
responseLoader := TStringList.Create;
try
try
// accept ranges didn't help
// request.Response.AcceptRanges := 'text/json';
// request.Response.AcceptRanges := 'text/xml';
urlRequest := DOMAIN + '?key=' + aVal;
request.Get(urlRequest, responseStream);
responseStream.Position := 0;
responseLoader.LoadFromStream(responseStream);
Result := responseLoader.Text;
except on E: Exception do
Result := e.Message;
end;
finally
responseLoader.Free;
responseStream.Free;
request.Free;
end;
end;
EDIT After the first answer I edited my function (still not working):
function DoRequest(aVal: string): string;
const DOMAIN = 'http://domain-location.com';
var
request: TIdHTTP;
responseStream: TMemoryStream;
responseLoader: TStringList;
urlRequest: string;
uri: TIdURI;
begin
request := TIdHTTP.Create(nil);
responseStream := TMemoryStream.Create;
responseLoader := TStringList.Create;
request.CookieManager := TIdCookieManager.Create(request);
uri := TIdURI.Create(DOMAIN);
try
try
// accept ranges didn't help
// request.Response.AcceptRanges := 'text/json';
// request.Response.AcceptRanges := 'text/xml';
urlRequest := DOMAIN + '?key=' + aVal;
request.CookieManager.AddServerCookie('cookie1', uri);
request.CookieManager.AddServerCookie('cookie2', uri);
request.CookieManager.AddServerCookie('cookie3', uri);
request.Get(urlRequest, responseStream);
responseStream.Position := 0;
responseLoader.LoadFromStream(responseStream);
Result := responseLoader.Text;
except on E: Exception do
Result := e.Message;
end;
finally
responseLoader.Free;
responseStream.Free;
request.Free;
end;
end;
And after I do the request the result is: HTTP1.1 403 Forbidden.
I inspected the page and the button I click is in a form like this:
<form action="http:/domiain.com" method="GET">
<input type="text" name="key">
<input type="submit" value="Click">
</form>
When I type http://domain-location.com?key=value there is no problem.
Any idea how to fix it?

The problem was with the UserAgent:
function DoRequest(aVal: string): string;
const DOMAIN = 'http://domain-location.com';
var
request: TIdHTTP;
urlRequest: string;
begin
request := TIdHTTP.Create(nil);
try
try
request.Request.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36';
urlRequest := DOMAIN + '?key=' + aVal;
Result := request.Get(urlRequest);
except on E: Exception do
Result := e.Message;
end;
finally
request.Free;
end;
end;

If cookies are involved, then you should GET the original HTML page first so the server can send whatever cookies it needs to be posted back when the button is "clicked", then you can GET the next page and let TIdHTTP post whatever cookies it had received.
Try this:
function DoRequest(const aVal: string): string;
const
DOMAIN = 'http://domain-location.com';
var
request: TIdHTTP;
begin
try
request := TIdHTTP.Create(nil);
try
request.Get(DOMAIN, TStream(nil)); // get cookies, discard HTML
Result := request.Get(DOMAIN + '?key=' + TIdURI.ParamsEncode(aVal));
finally
request.Free;
end;
except
on E: Exception do
Result := e.Message;
end;
end;

Check the actual request that is sent by your browser. 403 suggests that there is some sort of authentication going on. This might be a token or a cookie that your browser has, and sends with the request, but your sample client application may not have. Open the browser debugging panel to check the get request made by your browser, and compare it to the one made by your application. I bet there will be some difference.

Related

Delphi + Binance Api + Limit Order Problem Invalid signatur

{"code":3702,"msg":"Invalid signature.","timestamp":1623848681308}
i use trbinance.com api. I keep getting the same error
procedure TForm1.Button1Click(Sender: TObject);
var
url, sign, queryString, nonce: string;
ST: SystemTime;
DT: TDateTime;
uTime: int64;
sHour, sMin, sSec, sMili: Word;
stream, s_url: string;
Post: TStringList;
IdHTTP10 : TIDhttp;
api_key , api_secret : String;
begin
api_key := '**';
api_secret := '**';
GetSystemTime(ST);
DT := EncodeDate(ST.wYear, ST.wMonth, ST.wDay) +
EncodeTime(ST.wHour, ST.wMinute, ST.wSecond, ST.wMilliseconds);
uTime := DateUtils.MilliSecondsBetween(DT, UnixDateDelta);
nonce:=inttostr(uTime);
url :='https://trbinance.com/open/v1/orders';
queryString := 'symbol=BTT_TRY&side=0&type=1&quantity=1&price=0.0022&recvWindow=5000&timestamp='+nonce;
sign := THashSHA2.GetHMAC(queryString, api_secret, SHA256);
IdHTTP10 := TidHTTP.Create(nil);
IdHTTP10.HandleRedirects := True;
IdHTTP10.Request.CustomHeaders.Add('X-MBX-APIKEY:'+api_key);
IdHTTP10.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
Post:=TStringList.Create;
Post.Add('quantity=1&');
Post.Add('price=0.0022&');
Post.Add('recvWindow=5000&');
Post.Add('timestamp='+nonce+'&');
Post.Add('signature='+sign);
s_url := url + '?symbol=BTT_TRY&side=0&type=1';
try
stream:=IdHTTP10.POST(s_url, Post);
Memo1.Lines.Add(stream);
except
on E: Exception do
Memo1.Lines.Add(TimeToStr(time)+' <---> [Order] error: '+E.Message);
end;
IdHTTP10.Free;
Post.Free;
end;
Always problem :
{"code":3702,"msg":"Invalid signature.","timestamp":1623848681308}
Normaly Binance Symbol : BTTTRY
Turkey Api : BTT_TRY
Normaly Binance Order Url : httpps://api.binance.com/api/v3/order
Turkey Api Order Url : https://trbinance.com/open/v1/orders
Where is the Problem ?
Thank you so much.
Thank you for your answers. I made improvements to the code.
Friends in need can use this procedure in delphi. ( for Global Binance Api )
procedure TForm1.Button1Click(Sender: TObject);
const
Publickey= '***';
Secretkey= '***';
var
IdHTTP : TIdHTTP;
RBody : TStringstream;
QueryString : String;
Signature : String;
url : String;
begin
queryString :=
'symbol=BTTTRY&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1000&price=0.024400&recvWindow=5000&timestamp='
+ ABDULLAH_GetTimestamp();
Signature := THashSHA2.GetHMAC(queryString, Secretkey, SHA256);
Try
IdHTTP := TIdHTTP.Create();
RBody := TStringStream.Create(queryString + '&signature=' + Signature);
IdHTTP.Request.CustomHeaders.Add('X-MBX-APIKEY:' + Publickey);
IdHTTP.Request.CustomHeaders.UnfoldLines := True;
IdHTTP.Request.Accept := 'application/json';
IdHTTP.Request.ContentType := 'application/json';
IdHTTP.Request.ContentEncoding := 'utf-8';
IdHTTP.Request.BasicAuthentication := True;
IdHTTP.HandleRedirects := True;
IdHTTP.HTTPOptions := [hoKeepOrigProtocol, hoForceEncodeParams,
hoNoProtocolErrorException, hoWantProtocolErrorContent];
IdHTTP.iohandler := IdSSLIOHandlerSocketOpenSSL1;
url := 'https://api.binance.com/api/v3/order?';
Try
Memo1.Lines.Add(IdHTTP.Post(url, RBody));
Except
ON E: EXCEPTION DO
Memo1.Lines.Add('Error Message:' + E.Message);
End;
Finally
FreeAndNil(IdHTTP);
FreeAndNil(RBody);
End;
end;
This code works for Global Binance Api.
Change Symbol : BTTTRY -> BTT_TRY
And Post Url : https://api.binance.com/api/v3/order -> https://trbinance.com/open/v1/orders
İt's not working and Result :
{"code":3702,"msg":"Invalid signature.","timestamp":1723648673412}
This Procedure works in 'Global Binance Api' but not working 'trbinance Api'
Same procedure gives 'invalid signature' error in 'trbinance api'. :(
trbinance api example :
HMAC SHA256 signature:
[linux]$ echo -n "symbol=BTC_USDT&side=0&type=1&quantity=0.16&price=7500&timestamp=1581720670624&recvWindow=5000" | openssl dgst -sha256 -hmac "cfDC92B191b9B3Ca3D842Ae0e01108CBKI6BqEW6xr4NrPus3hoZ9Ze9YrmWwPFV"
(stdin)= 33824b5160daefc34257ab9cd3c3db7a0158a446674f896c9fc3b122ae656bfa
curl command:
(HMAC SHA256)
[linux]$ curl -H "X-MBX-APIKEY: cfDC92B191b9B3Ca3D842Ae0e01108CBKI6BqEW6xr4NrPus3hoZ9Ze9YrmWwPFV" -X POST 'https://www.trbinance.com/open/v1/orders' -d 'symbol=BTC_USDT&side=0&type=1&quantity=0.16&price=7500&timestamp=1581720670624&recvWindow=5000&signature=33824b5160daefc34257ab9cd3c3db7a0158a446674f896c9fc3b122ae656bfa'
There are a number of issues with this code, but the biggest one I see is that you should NOT be including trailing & characters in your TStringList strings, eg:
Post:=TStringList.Create;
Post.Add('quantity=1');
Post.Add('price=0.0022');
Post.Add('recvWindow=5000');
Post.Add('timestamp='+nonce);
Post.Add('signature='+sign);
TIdHTTP.Post() will insert those & characters into the request body for you, thus the data being transmitted is different than the data you signed.
According to Binance TR API docs:
Use your secretKey as the key and totalParams as the value for the HMAC operation.
totalParams is defined as the query string concatenated with the request body.
Error from API with the text "Invalid signature." could give you a clue that the problem is with your signature algo:
sign := THashSHA2.GetHMAC(queryString, api_secret, SHA256);
Your code here signs only your query string, but according to API docs you must sign query string concatenated with the request body.
So, you need to change your code like this ("Post" variable must be initialized before this line):
sign := THashSHA2.GetHMAC(queryString + Post.text, api_secret, SHA256);
I found that using the TIdHTTP component in Delphi to connect to Binance has problems. I kept getting Http/1.1 400 Bad Request.
I switched to TNetHTTPClient instead to connect with Binance.
The following is the working program that can be used for placing an order on Binance:
uses
System.Net.HttpClient, System.Hash, DateUtils;
procedure TForm1.fapiorder(oSide: String; oCoin: String; oAmt: Integer);
var
sOrderAmt: string;
ClearEntryUrl: string;
Params: string;
Signature: string;
URL: string;
ResponseStream : TStringStream;
postStream: TStringStream;
uTime: int64;
nowDateTime: string;
ST: SystemTime;
DT: TDateTime;
HttpClient: THttpClient;
HttpResponse: IHttpResponse;
BaseURLUSDM, ApiKey, ApiSecret: string;
begin
//https://binance-docs.github.io/apidocs/futures/cn/#trade-3
BaseURLUSDM := 'https://fapi.binance.com/'; //合約永續
ClearEntryUrl := 'fapi/v1/order';
ApiSecret := 'YOUR API SECRET KEY';
//timestamp 開始
GetSystemTime(ST);
DT := EncodeDate(ST.wYear, ST.wMonth, ST.wDay) + EncodeTime(ST.wHour,
ST.wMinute, ST.wSecond, ST.wMilliseconds);
uTime := DateUtils.MilliSecondsBetween(DT, UnixDateDelta);
nowDateTime := IntToStr(uTime);
Writeln('DateTime = ' + nowDateTime);
//timestamp 結束
Params := 'recvWindow=3000&timestamp=' + nowDateTime;
if (TDCoinMap.TryGetValue(oCoin, CoinMap) = True) then
begin
sOrderAmt := '0.07';
if (StrToFloat(sOrderAmt) <> 0) then
begin
Params := Params + '&symbol=' + oCoin;
Params := Params + '&side=' + oSide;
Params := Params + '&type=MARKET';
Params := Params + '&quantity=' + sOrderAmt;
Signature := THashSHA2.GetHMAC(Params, ApiSecret, SHA256);
URL := BaseURLUSDM + ClearEntryUrl + '?' + Params + '&signature=' + Signature;
postStream := TStringStream.Create('');
ResponseStream := TStringStream.Create('');
try
HttpClient := THTTPClient.Create;
HttpClient.HandleRedirects := True;
HttpClient.UserAgent := 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0';
HttpClient.ContentType := 'application/x-www-form-urlencoded';
HttpClient.CustomHeaders['X-MBX-APIKEY'] := 'Your Binance APIKEY';
HttpResponse := HttpClient.Post(URL, ResponseStream);
jResult := HttpResponse.ContentAsString();
Writeln('jResult = ' + jResult);
finally
HttpClient.Free;
end;
end;
end;
end;

Downloading XML fails using NetHTTPClient

I am using Delphi 10.2.3.
I want to download daily exchange rates from http://www.boi.org.il/currency.xml
My design time component setup:
NetHTTPClient1.AllowCookies := True;
NetHTTPClient1.HandleRedirects := True;
NetHTTPClient1.UserAgent := 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36';
NetHTTPRequest1.MethodString := 'GET';
NetHTTPRequest1.URL := 'http://www.boi.org.il/currency.xml';
My code is very simple:
XML := NetHTTPRequest1.Execute().ContentAsString();
What I get back in XML variable is:
<html><body><script>document.cookie='sssssss=6ed9ca3asssssss_6ed9ca3a; path=/';window.location.href=window.location.href;</script></body></html>
When I try to use a web browser (Opera in my case) I can see correct XML using same URL as above. I could not see what the problem is.
Any help is appreciated.
Edit:
After reading #NineBerry comments, I used Fiddler to watch each and every packet to the site. That showed me that browser is doing a request for three times before it can actually download XML. Second request, browser adding cookie reference in the response to first request. 3rd request is same as 2ns request.
After investigating below is a working code for me and I am not changing any TNetHTTPClient.UserAgent parameter:
function DownloadExchangeRates(const URL: string; out XML: string): Boolean;
var
Cookie: string;
Path: string;
AURI: TURI;
AClient: TNetHTTPClient;
ARequest: TNetHTTPRequest;
begin
AClient := nil;
ARequest := nil;
try
AClient := TNetHTTPClient.Create(nil);
AClient.AllowCookies := True;
AClient.HandleRedirects := True;
ARequest := TNetHTTPRequest.Create(nil);
ARequest.Client := AClient;
ARequest.Asynchronous := False;
ARequest.MethodString := 'GET';
ARequest.URL := URL;
ARequest.CustomHeaders['Pragma'] := 'no-cache';
try
XML := ARequest.Execute().ContentAsString();
if XML.Length > 5 then
begin
if UpperCase(XML.Substring(0, 6)) = '<HTML>' then
begin
Cookie := GetCookie(XML);
AURI := TURI.Create(URL);
Path := AURI.SCHEME_HTTP + '://' + AURI.Host + '/';
AClient.CookieManager.AddServerCookie(Cookie, Path);
AClient.CookieManager.AddServerCookie(Cookie, URL);
ARequest.CustomHeaders['Referer'] := URL;
XML := ARequest.Execute().ContentAsString();
if XML.Length > 5 then
begin
if UpperCase(XML.Substring(0, 6)) = '<HTML>' then
begin
XML := ARequest.Execute().ContentAsString();
end;
end;
end;
end;
except
on E: Exception do
begin
Exit(False);
end;
end;
finally
ARequest.Free();
AClient.Free();
end;
Result := (XML.Length > 2) and (XML[2] = '?');
end;
uses
System.Net.HttpClient;
function TUpdater.DownloadFile(const aURL: string; aStream: TStream): boolean;
var
vHTTP: THTTPClient;
begin
Assert(aStream <> nil);
vHTTP := THTTPClient.Create;
try
Result := vHTTP.Get(aURL, aStream).StatusCode = 200;
finally
vHTTP.Free;
end;
end;

Is there way to get the final URL after redirects using WinHTTP in Delphi?

I can use the following code to easily get the HTML source from the URL, but how can I get the actual URL itself? Because sometimes the initial URL goes through some redirects and the actual URL is not the same and I would like to capture it for usage. I cannot seem to find any good documentation on the usage of methods or properties for winHTTP in Delphi. Thanks!
var http: variant;
begin
http:=createoleobject('WinHttp.WinHttpRequest.5.1');
http.open('GET', 'http://URLtoWebsite.com', false);
http.send;
showmessage(http.responsetext);
end;
You can use something like this
function GetFinalURL(const AMainURL: string): string;
var
http: Variant;
begin
Result := '';
http := CreateOleObject('WinHttp.WinHttpRequest.5.1');
http.Option(6) := False;
http.open('GET', AMainURL, false);
http.send;
if http.Status = 302 then
Result := http.getResponseHeader('Location')
else
Result := AMainURL;
end;
Another way using Indy
function GetFinalURL(const AMainURL: string): string;
var
idHTTP: TIdHTTP;
begin
Result := '';
idHTTP := TIdHTTP.Create(nil);
try
idHTTP.HandleRedirects := True;
try
idHTTP.Get(AMainURL);
Result := idHTTP.Request.URL;
except
end;
finally
idHTTP.Free;
end;
end;
You can set WinHttpSetStatusCallback with WINHTTP_CALLBACK_FLAG_REDIRECT parameter to receive notifications about every redirect occurred during request.

403/Forbidden using delphi application to access my Google calendar

Can anyone tell me why i'm having trouble accessing my calendar information? I'm getting 403 forbidden.
procedure TForm1.Button1Click(Sender: TObject);
var
stringStream: TStringStream;
slPost, slReply: TStringList;
sPostResult: string;
begin
slPost := TStringList.Create;
slReply := TStringList.Create;
try
slPost.LineBreak := '&';
slPost.Values['Email'] := 'me#gmail.com';
slPost.Values['Passwd'] := 'pass';
slPost.Values['service'] := 'cl';
slPost.Values['source'] := 'company-program-version';
stringStream := TStringStream.Create(slPost.Text);
try
IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded';
sPostResult := IdHTTP1.Post('https://www.google.com/accounts/ClientLogin', stringStream);
slReply.LineBreak:=#10;
slReply.Text:=sPostResult;
slReply.LineBreak:=#13#10;
Memo1.Lines.Add(slReply.Text);
Memo1.Lines.Add('response=' + IdHTTP1.ResponseText);
// 200 OK
sPostResult := IdHTTP1.Post('https://www.google.com/accounts/ClientLogin', stringStream);
IdHTTP1.Request.CustomHeaders.FoldLines:=false;
IdHTTP1.Request.CustomHeaders.Clear;
IdHTTP1.Request.CustomHeaders.Values['GData-Version']:='2.0';
IdHTTP1.Request.CustomHeaders.Values['Authorization']:='GoogleLogin auth=' + slReply.Values['auth'];
(* custom headers:
GData-Version: 2.0
Authorization: GoogleLogin (line continues) auth=DQwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhateverwhatever *)
IdHTTP1.Request.ContentType := 'application/atom+xml';
// 403 Forbidden
memo1.Lines.Add(IdHTTP1.Get('https://www.googleapis.com/calendar/v3/users/me/calendarList'));
finally
stringStream.Free;
end;
finally
slPost.Free;
slReply.Free;
end;
end;
thank you!
mp
After some reading, I think you need to deal with Redirect. So If response is redirect, get the new url, reattach the authorization to the new request header with the new url. Otherwise your redirection request will be missing the required authorization and give you 403 error.

HTTPS post - what I'm doing wrong?

I'm making requests to the webaddress to get XML files throught the HTTPS connection. But this connection works like 50%. In most cases it fails. Usual error is "socket error #10060". Or "Error connecting with SSL. EOF was observed that violates the protocol". What I'm doing wrong?
function SendRequest(parameters: string): IXMLDocument;
var
sPostData: TStringList;
sHttpSocket: TIdHTTP;
sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
resStream: TStringStream;
xDoc: IXMLDocument;
begin
sPostData := TStringList.Create;
try
sPostData.Add('add some parameter to post' + '&');
sPostData.Add('add some parameter to post' + '&');
sPostData.Add('add some parameter to post' + '&');
sPostData.Add(parameters);
sHttpSocket := TIdHTTP.Create;
sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create;
sHttpSocket.IOHandler := sshSocketHandler;
sHttpSocket.Request.ContentType := 'application/x-www-form-urlencoded';
sHttpSocket.Request.Method := 'POST';
resStream := TStringStream.Create;
sHttpSocket.Post(Self.sUrl, sPostData, resStream);
xDoc := CreateXMLDoc;
xDoc.LoadFromStream(resStream);
Result := xDoc;
resStream.Free;
sHttpSocket.Free;
sshSocketHandler.Free;
sPostData.Free;
except on E: Exception do
begin
TCommon.ErrorLog('errorLog.txt', DateTimeToStr(Now) + ' ' + E.Message);
end
end;
end;
Maybe I can do this in another way, that works like 100%, when internet connection is available?
Regards,
evilone
An "EOF" error suggests you are connnecting to a server that is not actually using SSL to begin with, or the SSL data may be corrupted.
Besides that, why are you including explicit '&' characters between your post data parameters? Don't do that, Indy will just encode them and send its own '&' characters. Also, consider using TMemoryStream instead of TStringStream to ensure IXMLDocumect.LoadFromStream() is loading the server's original raw XML data as-is, and not an altered version that the RTL/VCL produces due to Unicode handling (TStringStream is TEncoding-enabled).
Edit: Given the URL you provided, an example of calling verifyUser() would look like this:
const
ERPLYAccountCode = '...';
function verifyUser(const user, pass: string; const sessionLength: Integer = 3600): IXMLDocument;
var
sPostData: TStringList;
sHttpSocket: TIdHTTP;
sshSocketHandler: TIdSSLIOHandlerSocketOpenSSL;
resStream: TMemoryStream;
xDoc: IXMLDocument;
begin
Result := nil;
try
resStream := TMemoryStream.Create;
try
sPostData := TStringList.Create;
try
sPostData.Add('clientCode=' + ERPLYAccountCode);
sPostData.Add('request=verifyUser');
sPostData.Add('version=1.0');
sPostData.Add('responseType=XML');
sPostData.Add('responseMode=normal');
sPostData.Add('username=' + user);
sPostData.Add('password=' + pass);
sPostData.Add('sessionLength=' + IntToStr(sessionLength));
sHttpSocket := TIdHTTP.Create;
try
sshSocketHandler := TIdSSLIOHandlerSocketOpenSSL.Create(sHttpSocket);
sHttpSocket.IOHandler := sshSocketHandler;
sHttpSocket.Request.ContentType := 'application/x-www-form-urlencoded';
sHttpSocket.Post('https://www.erply.net/api/', sPostData, resStream);
finally
sHttpSocket.Free;
end;
finally
sPostData.Free;
end;
resStream.Position := 0;
xDoc := CreateXMLDoc;
xDoc.LoadFromStream(resStream);
Result := xDoc;
finally
resStream.Free;
end;
except
on E: Exception do
begin
TCommon.ErrorLog('errorLog.txt', DateTimeToStr(Now) + ' ' + E.Message);
end;
end;
end;

Resources