Prevent erroneously long strings from being transferred

Hello,

I have various services that accept string parameters, or structures with string fields and this works fine.
However, I have some client software that are exhibiting strange behavior during their development phase and which send ridiculously long strings on some string parameters.
Looking at the actual string content, it’s clearly a memory dump from the client process, and while this needs to be fixed, the server part is not too happy with such received data.

In order to harden my servers against this kind of “rogue” client, I would like to give a limit to the size of strings that can be sent and/or received during communication.

I could not see anything in TROSerializer.WriteUnicodeString so I believe this is not supported on the client side, but TROSerializer.ReadUnicodeString has an optional parameter called iMaxLength that, if not equal to -1 serves this exact purpose

Debugging a data exchange, I see that, sadly, TROSerializer.ReadUnicodeString is called from TROSerializer.Read with no way to give a value to iMaxLength so it seems I’m stuck here.

Did I miss something? Is there another parameter that controls this?

What problems is the server having? Is iOS that your one services’ code simply doesnt expect strings this long, or is something failing on there RO level? Memory limitations aside, the SDK itself should handle any length of strings on the server side (although I do like the idea of being able to enforce a limit).

Right now, the quickest option might be to limit the message size top something reasonable; a message with too long strings would be rejected based on there wntiore message itself being too long, then. Note that you can set a limit for the wire size (i.e. bytes received) and the decompressed size (ie after decompressing). You;d want to limit the latter, since in theory a verey long string could still compress to a very small package, of the string is repetitive enough.

That said, I will log a feature request for limiting string sizes (probably as a single global flag on the message though, not in the RODL per individual parameter — if that sounds reasonable — as this would be magnitudes easier to implement on that level, I expect)…

Your server and client are both Delphi I take it (although I’d log the issue for both/all platforms).

Yeah this is probably there for different reasons; right now the deserialization has no source for any value to pass there, so it’ll allow unlimited size.

—marc

Logged as bugs://D19265.

RO SDK is just fine but it tries to instantiate strings that are too close to the 2^31-1 size limit and this gives me EOutOfMemory exceptions.
However, that’s the luckiest situation, most often it stores the overly long strings and they end up being used as is which has very annoying consequences. For instance, some are logged into file and this eats up lots of disk space…

That would indeed be just fine, but I could not find a property on the TROBinMessage to change this. However, there is the OnDecompress event that has a OriginalSize parameter, so I guess I could use that to implement the limit.

A global flag is definitely good for me, this would be a “last line of defense” situation anyway.

The server is Delphi but clients are Delphi or C#.

Okay, cool. so on SDK level all is behaving as should, for now (bit I agree adding a limit there would be good).

I’ll have to leave this for Eugene to answer. I do know its there somewhere…

Okay, cool. ideally, we’d add this to all platforms, and on the client and server.

Hi,

as a temporary solution, you can create TROMessage descendant like

type
  TMyBinMessage = class(TROBinMessage)
  protected
    function CreateSerializer: TROSerializer; override;
  end;

  TMyStreamSerializer = class(TROStreamSerializer)
  public
    procedure ReadUnicodeString(const aName: string; var Ref; ArrayElementId: Integer = -1; aMaxLength: Integer = -1); override;
  end;

function TMyBinMessage.CreateSerializer: TROSerializer;
begin
  Result := TMyStreamSerializer.Create(nil);
end;

procedure TMyStreamSerializer.ReadUnicodeString(const aName: string; var Ref;
  ArrayElementId, aMaxLength: Integer);
const
  l_maxSize = 10*1024*1024; // 10mb
begin
  inherited ReadUnicodeString(aName, Ref, ArrayElementId, l_maxSize);
end;

and use it instead of TROBinMesage

1 Like

Thanks, that’s indeed a neat idea there. I implemented it in the client side, though, with the following code:

procedure TMyStreamSerializer.WriteUnicodeString(const aName: string; const Ref;
  ArrayElementId: integer);
const
  MaxStringLength = 256;
  StringExtractLength = 50;
var
  RefAsString: string;
  StringLength: Integer;
begin
  RefAsString := UnicodeString(Ref);
  StringLength := Length(RefAsString);

  if StringLength > MaxStringLength then
    raise EROException.CreateFmt('String for [%s] is too long: %d > %d - First %d characters = %s', [aName, StringLength, MaxStringLength, StringExtractLength, Copy(RefAsString, 1, StringExtractLength)]);

  inherited WriteUnicodeString(aName, Ref, ArrayElementId);
end;

I did it this way because it saves transfer time, and also because in the server, all components are on a DataModule which makes replacing them with manual instantiation much less easy.

Hi,

it can be like

  ROMessage := TMyBinMessage.Create(nil);
  TROMessageDispatcher(ROServer.Dispatchers.Add).Message := ROMessage;
  ROServer.Active := true;