From 16e6a6619af573446e08e6af7a87cb8578526989 Mon Sep 17 00:00:00 2001 From: pypalkar23 <7241388+pypalkar23@users.noreply.github.com> Date: Wed, 7 Dec 2022 11:42:18 -0500 Subject: [PATCH 1/7] Added new snippets in memory management --- src/memory-management/README.md | 155 +++++++++++++++++++++++++++++++- 1 file changed, 154 insertions(+), 1 deletion(-) diff --git a/src/memory-management/README.md b/src/memory-management/README.md index 00fd695..7d63f1c 100644 --- a/src/memory-management/README.md +++ b/src/memory-management/README.md @@ -83,4 +83,157 @@ Quoting [Golang's Github](https://github.com/golang/go/issues/13761): [1]: ../input-validation/README.md [2]: ../output-encoding/README.md -[3]: ../error-handling-logging/README.md +[3]: ../error-handling-logging/README. + +## Memory Leaking Scenarios +Even though go is a memory-safe language. The go compiler has been written in a way that can cause it to kind-of memory leaking issues sometimes. Since memory leaking is one of the ways using which an attacker can launch attacks like DDoS, one needs to be aware of recommended practices to prevent writing programs that can lead to memory leaking.Let's look at some of the scenarios. + +## Memory leak. because of substrings: +For example, in the below function the memory occupied by the paramStr variable won't be garbage collected even though the function has returned as the strSlice variable is sharing the same underlying memory block. + +```go +var strSlice string //variable of type string on heap + +func customizedSubstring(paramStr string) { + strSlice = paramStr[:100] + /* + Since strSlice is created by slicing the paramStr, both the variables + share the same underlying memory block. Even though paramStr's lifespan + finishes after the function has returned, that memory can't be garbage + collected as some chunk of it's memory(100 bytes) is in use. + */ +} + +``` +To get around such scenario, we can opt for multiple ways to efficiently copy a string, few of those are listed below: +## Using a byte array + +```go +func customizedSubstring(paramStr string) { + tempByteArray := []byte(paramStr[:100]) + strSlice = string(tempByteArray) + /* + A two step process where we first convert the string to a byte array whic + then convert that intermediate byte array to a string again. This way the + resulting slice gets created at new memory block. + */ +} +``` + +# Concatenation +```go +func customizedSubstring(paramStr string) { + strSlice = (" " + paramStr[:100][:1]) + /* + The use of empty string makes the compiler creates a new memory block for + resulting string at the cost of 1 extra byte. + */ +} +``` + +# Using a string Builder +```go +func customizedSubstring(paramStr string) { + var temp strings.Builder + temp.Grow(100) + temp.WriteString(paramStr[:100]) + strSlice = temp.String() + + /* + A slightly verbose way of making sure that sliced string gets created at a new memory + location and the source string can be collected by the garbage collector. + */ +} +``` + +## Memory Leaking with Pointers: +Let's look at the function below. Once the function's lifespan is over, the memory reference allocated for the +the first and the last elements of the slice will be lost. + +```go +func slicePointerFunc() []*string { + new_slice := []*string{new(string), new(string), new(string), new(string), new(string), new(string)} + //..... + return new_slice[1:4:4] + /*As long as this returned slice is been in use in the other parts of the program + the underling slice can't be garbage collected. Subsequently preventing first and + last elements of the slice from being garbage collected*/ +} +``` + +To avoid such memory leaking, we can reset the unused pointers by simply setting them to nil. + +```go +func slicePointerFunc() []*string { + new_slice := []*string{new(string), new(string), new(string), new(string), new(string), new(string)} + //.... + new_slice[0], new_slice[len(new_slice)-1] = nil, nil + /*Marking the unused elements of slice as nil, will make them available for garbage collection */ + return new_slice[1:4:4] +} +``` + +## Memory leaking because of deferred function calls: +Sometimes a deferred call queue can consume significant memory and may hold on to resources which are needed by other programs/subprograms running on the system. For example, look at the below program, which writes to number of files during it's lifespan. +Such program might hold on to the file handlers till the very end of the function, even after finishing updating those files much earlier. +```go +func multiFileWrites(fileList []FileDetails) error { + for _, entry := range fileList { + file, err := os.Open(entry.Path) + if err != nil { + return err + } + + defer file.Close() + /* Such approach may result in large number of deferred calls in case + of thousands of file writes. Subsequently delaying the release of system + resources. + */ + _, err = file.WriteString(entry.Content) + if err != nil { + return err + } + + err = file.Sync() + if err != nil { + return err + } + } + + return nil +} +``` + +So, the solution in such cases is to use an anonymous function that will enclose the deferred calls to make them execute relatively earlier. + +```go +func memoryEfficientMultipleFileWrites(fileList []FileDetails) error { + for _, entry := range fileList { + if err := func() error { + file, err := os.Open(entry.Path) + if err != nil { + return err + } + + /* + Use of anonymous function makes sure that the every deferred call + for file close gets called at the end of every iteration instead of + processing every call at the end of the outer function. + */ + defer file.Close() + + _, err = file.WriteString(entry.Content) + if err != nil { + return err + } + + return file.Sync() + }(); err != nil { + return err + } + + } + + return nil +} +``` From e7f4998e03bae6edd27608f588b6fe6896a47a9f Mon Sep 17 00:00:00 2001 From: pypalkar23 <7241388+pypalkar23@users.noreply.github.com> Date: Wed, 7 Dec 2022 12:46:25 -0500 Subject: [PATCH 2/7] Added article related to gRPC --- src/communication-security/README.md | 1 + .../grpc-code/grpc_client_secured/client.go | 57 +++++ .../grpc-code/grpc_server_secured/server.go | 51 +++++ .../grpc-code/samplebuff/sample.pb.go | 212 ++++++++++++++++++ .../grpc-code/samplebuff/sample.proto | 18 ++ .../grpc-code/samplebuff/sample_grpc.pb.go | 105 +++++++++ src/communication-security/grpc.md | 102 +++++++++ 7 files changed, 546 insertions(+) create mode 100644 src/communication-security/grpc-code/grpc_client_secured/client.go create mode 100644 src/communication-security/grpc-code/grpc_server_secured/server.go create mode 100644 src/communication-security/grpc-code/samplebuff/sample.pb.go create mode 100644 src/communication-security/grpc-code/samplebuff/sample.proto create mode 100644 src/communication-security/grpc-code/samplebuff/sample_grpc.pb.go create mode 100644 src/communication-security/grpc.md diff --git a/src/communication-security/README.md b/src/communication-security/README.md index cd5afab..f3e69f2 100644 --- a/src/communication-security/README.md +++ b/src/communication-security/README.md @@ -13,5 +13,6 @@ The scope of this section covers the following communication channels: * HTTP/HTTPS * Websockets +* gRPC [1]: https://www.owasp.org/index.php/Man-in-the-middle_attack diff --git a/src/communication-security/grpc-code/grpc_client_secured/client.go b/src/communication-security/grpc-code/grpc_client_secured/client.go new file mode 100644 index 0000000..5398528 --- /dev/null +++ b/src/communication-security/grpc-code/grpc_client_secured/client.go @@ -0,0 +1,57 @@ +package main + +import ( + "context" + "crypto/tls" + "crypto/x509" + "flag" + "fmt" + "io/ioutil" + "log" + pb "pentest/grpc/samplebuff" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +const ( + defaultName = "Art Rosenbaum" +) + +var ( + addr = flag.String("addr", "localhost:10001", "Address of Server") + name = flag.String("name", defaultName, "Name to greet") +) + +func main() { + flag.Parse() + b, _ := ioutil.ReadFile("../cert/ca.cert") + cp := x509.NewCertPool() + if !cp.AppendCertsFromPEM(b) { + fmt.Println("credentials: failed to append certificates") + } + + config := &tls.Config{ + InsecureSkipVerify: false, + RootCAs: cp, + } + + creds := credentials.NewTLS(config) + // Set up a connection to the server. + conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(creds)) + if err != nil { + log.Fatalf("Could not connect to server: %v", err) + } + defer conn.Close() + c := pb.NewSampleServiceClient(conn) + + // Contact the server and print out its response. + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + r, err := c.Greet(ctx, &pb.SendMsg{Name: *name}) + if err != nil { + log.Fatalf("could not send message: %v", err) + } + log.Printf("Sending message: %s", r.GetMessage()) +} diff --git a/src/communication-security/grpc-code/grpc_server_secured/server.go b/src/communication-security/grpc-code/grpc_server_secured/server.go new file mode 100644 index 0000000..d555908 --- /dev/null +++ b/src/communication-security/grpc-code/grpc_server_secured/server.go @@ -0,0 +1,51 @@ +package main + +import ( + "context" + "flag" + "fmt" + "log" + "net" + + pb "pentest/grpc/samplebuff" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" +) + +var ( + port = flag.Int("port", 10001, "The server port") +) + +// server is used to implement sample.GreeterServer. +type server struct { + pb.UnimplementedSampleServiceServer +} + +// Greet implements sample.GreeterServer +func (s *server) Greet(ctx context.Context, in *pb.SendMsg) (*pb.SendResp, error) { + log.Printf("Received msg: %v", in.GetName()) + return &pb.SendResp{Message: "Hey " + in.GetName()}, nil +} + +func main() { + flag.Parse() + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) + if err != nil { + log.Fatalf("Could not start the server: %v", err) + } + + //Configuring the certificates + creds, err := credentials.NewServerTLSFromFile("../cert/service.pem", "../cert/service.key") + + if err != nil { + log.Fatalf("TLS setup failed: %v", err) + } + + s := grpc.NewServer(grpc.Creds(creds)) + pb.RegisterSampleServiceServer(s, &server{}) + log.Printf("Server started at: %v", lis.Addr()) + if err := s.Serve(lis); err != nil { + log.Fatalf("Could not start the server: %v", err) + } +} diff --git a/src/communication-security/grpc-code/samplebuff/sample.pb.go b/src/communication-security/grpc-code/samplebuff/sample.pb.go new file mode 100644 index 0000000..33a2fd7 --- /dev/null +++ b/src/communication-security/grpc-code/samplebuff/sample.pb.go @@ -0,0 +1,212 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.27.1 +// protoc v3.21.9 +// source: samplebuff/sample.proto + +package sample + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type SendMsg struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *SendMsg) Reset() { + *x = SendMsg{} + if protoimpl.UnsafeEnabled { + mi := &file_samplebuff_sample_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendMsg) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendMsg) ProtoMessage() {} + +func (x *SendMsg) ProtoReflect() protoreflect.Message { + mi := &file_samplebuff_sample_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendMsg.ProtoReflect.Descriptor instead. +func (*SendMsg) Descriptor() ([]byte, []int) { + return file_samplebuff_sample_proto_rawDescGZIP(), []int{0} +} + +func (x *SendMsg) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type SendResp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` +} + +func (x *SendResp) Reset() { + *x = SendResp{} + if protoimpl.UnsafeEnabled { + mi := &file_samplebuff_sample_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SendResp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendResp) ProtoMessage() {} + +func (x *SendResp) ProtoReflect() protoreflect.Message { + mi := &file_samplebuff_sample_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendResp.ProtoReflect.Descriptor instead. +func (*SendResp) Descriptor() ([]byte, []int) { + return file_samplebuff_sample_proto_rawDescGZIP(), []int{1} +} + +func (x *SendResp) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +var File_samplebuff_sample_proto protoreflect.FileDescriptor + +var file_samplebuff_sample_proto_rawDesc = []byte{ + 0x0a, 0x17, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x62, 0x75, 0x66, 0x66, 0x2f, 0x73, 0x61, 0x6d, + 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x73, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x22, 0x1d, 0x0a, 0x07, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, 0x67, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x22, 0x24, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x18, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x3b, 0x0a, 0x0d, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, + 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x47, 0x72, 0x65, 0x65, 0x74, + 0x12, 0x0f, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x4d, 0x73, + 0x67, 0x1a, 0x10, 0x2e, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, + 0x65, 0x73, 0x70, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x70, 0x79, 0x70, 0x61, 0x6c, 0x6b, 0x61, 0x72, 0x32, 0x33, 0x2f, 0x67, 0x6f, 0x2d, + 0x72, 0x70, 0x63, 0x2d, 0x63, 0x69, 0x73, 0x35, 0x32, 0x30, 0x39, 0x2f, 0x73, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_samplebuff_sample_proto_rawDescOnce sync.Once + file_samplebuff_sample_proto_rawDescData = file_samplebuff_sample_proto_rawDesc +) + +func file_samplebuff_sample_proto_rawDescGZIP() []byte { + file_samplebuff_sample_proto_rawDescOnce.Do(func() { + file_samplebuff_sample_proto_rawDescData = protoimpl.X.CompressGZIP(file_samplebuff_sample_proto_rawDescData) + }) + return file_samplebuff_sample_proto_rawDescData +} + +var file_samplebuff_sample_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_samplebuff_sample_proto_goTypes = []interface{}{ + (*SendMsg)(nil), // 0: sample.SendMsg + (*SendResp)(nil), // 1: sample.SendResp +} +var file_samplebuff_sample_proto_depIdxs = []int32{ + 0, // 0: sample.SampleService.Greet:input_type -> sample.SendMsg + 1, // 1: sample.SampleService.Greet:output_type -> sample.SendResp + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_samplebuff_sample_proto_init() } +func file_samplebuff_sample_proto_init() { + if File_samplebuff_sample_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_samplebuff_sample_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendMsg); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_samplebuff_sample_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SendResp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_samplebuff_sample_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_samplebuff_sample_proto_goTypes, + DependencyIndexes: file_samplebuff_sample_proto_depIdxs, + MessageInfos: file_samplebuff_sample_proto_msgTypes, + }.Build() + File_samplebuff_sample_proto = out.File + file_samplebuff_sample_proto_rawDesc = nil + file_samplebuff_sample_proto_goTypes = nil + file_samplebuff_sample_proto_depIdxs = nil +} diff --git a/src/communication-security/grpc-code/samplebuff/sample.proto b/src/communication-security/grpc-code/samplebuff/sample.proto new file mode 100644 index 0000000..25ce19b --- /dev/null +++ b/src/communication-security/grpc-code/samplebuff/sample.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; + +option go_package = "github.com/pypalkar23/go-rpc-cis5209/sample"; + +package sample; + + +service SampleService { + rpc Greet (SendMsg) returns (SendResp); +} + +message SendMsg { + string name = 1; +} + +message SendResp{ + string message = 1; +} \ No newline at end of file diff --git a/src/communication-security/grpc-code/samplebuff/sample_grpc.pb.go b/src/communication-security/grpc-code/samplebuff/sample_grpc.pb.go new file mode 100644 index 0000000..8d6bbd7 --- /dev/null +++ b/src/communication-security/grpc-code/samplebuff/sample_grpc.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.9 +// source: samplebuff/sample.proto + +package sample + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// SampleServiceClient is the client API for SampleService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type SampleServiceClient interface { + Greet(ctx context.Context, in *SendMsg, opts ...grpc.CallOption) (*SendResp, error) +} + +type sampleServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewSampleServiceClient(cc grpc.ClientConnInterface) SampleServiceClient { + return &sampleServiceClient{cc} +} + +func (c *sampleServiceClient) Greet(ctx context.Context, in *SendMsg, opts ...grpc.CallOption) (*SendResp, error) { + out := new(SendResp) + err := c.cc.Invoke(ctx, "/sample.SampleService/Greet", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// SampleServiceServer is the server API for SampleService service. +// All implementations must embed UnimplementedSampleServiceServer +// for forward compatibility +type SampleServiceServer interface { + Greet(context.Context, *SendMsg) (*SendResp, error) + mustEmbedUnimplementedSampleServiceServer() +} + +// UnimplementedSampleServiceServer must be embedded to have forward compatible implementations. +type UnimplementedSampleServiceServer struct { +} + +func (UnimplementedSampleServiceServer) Greet(context.Context, *SendMsg) (*SendResp, error) { + return nil, status.Errorf(codes.Unimplemented, "method Greet not implemented") +} +func (UnimplementedSampleServiceServer) mustEmbedUnimplementedSampleServiceServer() {} + +// UnsafeSampleServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to SampleServiceServer will +// result in compilation errors. +type UnsafeSampleServiceServer interface { + mustEmbedUnimplementedSampleServiceServer() +} + +func RegisterSampleServiceServer(s grpc.ServiceRegistrar, srv SampleServiceServer) { + s.RegisterService(&SampleService_ServiceDesc, srv) +} + +func _SampleService_Greet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SendMsg) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(SampleServiceServer).Greet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/sample.SampleService/Greet", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(SampleServiceServer).Greet(ctx, req.(*SendMsg)) + } + return interceptor(ctx, in, info, handler) +} + +// SampleService_ServiceDesc is the grpc.ServiceDesc for SampleService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var SampleService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "sample.SampleService", + HandlerType: (*SampleServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Greet", + Handler: _SampleService_Greet_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "samplebuff/sample.proto", +} diff --git a/src/communication-security/grpc.md b/src/communication-security/grpc.md new file mode 100644 index 0000000..3a58565 --- /dev/null +++ b/src/communication-security/grpc.md @@ -0,0 +1,102 @@ +gRPC +========== +gRPC is a new open source high performance Remote Procedure Call(RPC) framework that can be used in number of scenarios such as distributed computing, mobile & web applications. +gRPC is one of the popular ways to make microservices implemented in different languages/technolgies to seemlessly interact with each other with protocol buffers. Go provides inbuilt support for [gRPC][1]. + +Let's look at how we can implement secure gRPC calls using TLS Encryption on both client and server side. + +### Secure gRPC Server: +The complete code can be found [here][2]. +```go +var ( + port = flag.Int("port", 10001, "The server port") +) + +// server is used to implement sample.GreeterServer. +type server struct { + pb.UnimplementedSampleServiceServer +} + +// Greet implements sample.GreeterServer +func (s *server) Greet(ctx context.Context, in *pb.SendMsg) (*pb.SendResp, error) { + log.Printf("Received msg: %v", in.GetName()) + return &pb.SendResp{Message: "Hey " + in.GetName()}, nil +} + +func main() { + flag.Parse() + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) + if err != nil { + log.Fatalf("Could not start the server: %v", err) + } + + //Reading the certificate key and private key needed for credentials + creds, err := credentials.NewServerTLSFromFile("../cert/service.pem", "../cert/service.key") + + if err != nil { + log.Fatalf("TLS setup failed: %v", err) + } + + s := grpc.NewServer(grpc.Creds(creds)) + pb.RegisterSampleServiceServer(s, &server{}) + log.Printf("Server started at: %v", lis.Addr()) + if err := s.Serve(lis); err != nil { + log.Fatalf("Could not start the server: %v", err) + } +} +``` + +### Secure gRPC client: +The complete code can be found [here][3]. +```go +const ( + defaultName = "Art Rosenbaum" +) + +var ( + addr = flag.String("addr", "localhost:10001", "Address of Server") + name = flag.String("name", defaultName, "Name to greet") +) + +func main() { + flag.Parse() + b, _ := ioutil.ReadFile("../cert/ca.cert") + cp := x509.NewCertPool() + if !cp.AppendCertsFromPEM(b) { + fmt.Println("credentials: failed to append certificates") + } + + config := &tls.Config{ + InsecureSkipVerify: false, + RootCAs: cp, + } + + creds := credentials.NewTLS(config) + // Set up a connection to the server. + conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(creds)) + if err != nil { + log.Fatalf("Could not connect to server: %v", err) + } + defer conn.Close() + c := pb.NewSampleServiceClient(conn) + + // Contact the server and print out its response. + ctx, cancel := context.WithTimeout(context.Background(), time.Second) + defer cancel() + r, err := c.Greet(ctx, &pb.SendMsg{Name: *name}) + if err != nil { + log.Fatalf("could not send message: %v", err) + } + log.Printf("Sending message: %s", r.GetMessage()) +} + +``` + +**Important** : As mentioned in the previous sections that the TLS certificates should be valid, should not be expired and should be installed with intermediate certificates when required as recommended in OWASP SCP Quick +Reference Guide][4]. +Also, an ideal gRPC client should avoid connecting to a gRPC server which is missing or has invalid certificates. The `InsecureSkipVerify` flag should never be set to false for services deployed in production. + +[1]: https://pkg.go.dev/google.golang.org/grpc +[2]: ./grpc-code/grpc_server_secured/server.go +[3]: ./grpc-code/grpc_client_secured/client.go +[4]: https://www.owasp.org/images/0/08/OWASP_SCP_Quick_Reference_Guide_v2.pdf From a8b6ebccd1620667edbd58a421da470cbd8f7e77 Mon Sep 17 00:00:00 2001 From: pypalkar23 <7241388+pypalkar23@users.noreply.github.com> Date: Wed, 7 Dec 2022 15:10:25 -0500 Subject: [PATCH 3/7] Adding Chapter for Process Management --- src/memory-management/README.md | 4 +- src/process-management/README.md | 224 +++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 src/process-management/README.md diff --git a/src/memory-management/README.md b/src/memory-management/README.md index 7d63f1c..6785a7e 100644 --- a/src/memory-management/README.md +++ b/src/memory-management/README.md @@ -83,7 +83,7 @@ Quoting [Golang's Github](https://github.com/golang/go/issues/13761): [1]: ../input-validation/README.md [2]: ../output-encoding/README.md -[3]: ../error-handling-logging/README. +[3]: ../error-handling-logging/README.md ## Memory Leaking Scenarios Even though go is a memory-safe language. The go compiler has been written in a way that can cause it to kind-of memory leaking issues sometimes. Since memory leaking is one of the ways using which an attacker can launch attacks like DDoS, one needs to be aware of recommended practices to prevent writing programs that can lead to memory leaking.Let's look at some of the scenarios. @@ -204,7 +204,7 @@ func multiFileWrites(fileList []FileDetails) error { } ``` -So, the solution in such cases is to use an anonymous function that will enclose the deferred calls to make them execute relatively earlier. +So, the solution in such cases is to use an anonymous function that will enclose the deferred calls to make them execute relatively earlier rather than waiting till all the files have been processed. ```go func memoryEfficientMultipleFileWrites(fileList []FileDetails) error { diff --git a/src/process-management/README.md b/src/process-management/README.md new file mode 100644 index 0000000..9790b8e --- /dev/null +++ b/src/process-management/README.md @@ -0,0 +1,224 @@ +Process Management +================== +Goroutines is one of the major reasons why Go has been one of the most widely language since past few years. +Goroutines are lightweight thread managed by go runtime which allows developers to write concurrent programs in go. +The cost of goroutines are considerably lighter than a traditional thread and it's very common for go applications to +have thousands of goroutines concurrently. + + +However, one of the common problems with goroutines is that a badly written goprogram might produce **hanging** or **zombie** goroutines +which might hold on to the system resources and keep consuming memory. This might cause problems for large-scale services as attacker might +launch attacks which might lead to creation of such hanging Goroutines in large numbers, subsequently leading to putting load on CPU and memory and negatively affecting availablity of the applicaton altogether. Let's look at what hanging goroutines are and ways to avoid those. + +Here is an example of what an hanging goroutine looks like: +```go +func leakingGoroutine(ch chan string) { + val := <-ch + fmt.Println(val) +} + +func handler() { + ch := make(chan string) + + go leakingGoroutine(ch) + return +} +``` + +Here even if the handler returns the Goroutine continues live in the background waiting for data to be sent over channel, which will never happen. + +There are two common patterns which can result in such leaks. Those patterns are as follows: +1. The Forgotten sender +2. The Abandoned receiver + +## The Forgotten Sender +This happens when the sender is blocked because no receiver is waiting on the channel to receive the data. +```go +func danglingSender(ch chan string) { + val := "Brian" + // This will be blocked as no receiver is available to receive the data + ch <- val +} + +func handler() { + ch := make(chan string) + + go danglingSender(ch) + return +} +``` + +Such thing can happen in following scenarios. +**Wrong use of Context** + +```go +func danglingSender(ch chan string) { + //simulated async function + val := someAsyncNetworkCall() + ch <- val +} + +func handlerWithContext() error { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) + defer cancel() + + ch := make(chan string) + go danglingSender(ch) + + select { + case val := <-ch: + { + fmt.Printf("Value Received :%s", val) + } + case <-ctx.Done(): + { + return errors.New("Timeout! Exiting") + } + } + return nil +} + +``` +The above program tries to simulate web service handler. We have sent a context that will issue a timeout after 10ms. Then we spawn a Goroutine that will asynchronous network call. +In case of the network call taking more time than expected and a timeout happens, the program will run run `case <- ctx.Done()` and the handler will return an error. +As soon as the handler returns the danglingSender function will be blocked as there is no one to receive the val, which will never happen. + +**Wrong placement of Receiver** +```go +func danglingSender(ch chan string) { + val := someAsyncNetworkCall() + ch <- val +} + +func handler() error { + ch := make(chan string) + go danglingSender(ch) + + err := validateSomeData() + + if err != nil { + return errors.New("Validation error.Exiting") + } + + data := <-ch + fmt.Println(data) + return nil +} +``` + +Here we spawn a Goroutine that makes a network call and simultenously move to some other validation logic. If the validation returns error than the handler exits. And as a consequence the Goroutine gets blocked forever as there is nothing at the receiving end of the channel. + + +**Solution to The Forgotten Sender** +To summarize the forgotten sender happens as no receiver is present on the other side to receive the data. And the root cause is the unbuffered channel we have been using. +An unbuffered channel requires a receiver as soon as the message is sent on the channel otherwise the sender is blocked. So, the solution is to buffered channel, where we specifiy the capacity of the channel while initializing it. This way the sender can send data into the channel without requiring a receiver. + +```go +func danglingSender(ch chan string) { + val := "Brian" + // This won't be blocked + ch <- val +} + +func handler() { + ch := make(chan string,1) + + go danglingSender(ch) + return +} +``` + +## The Abandoned Receiver +This is exact opposite of previous pattern. Here the receiver gets blocked as no sender is sending the data from other side. + +```go +func danglingReceiver(ch chan int) { + //This will block + val := <-ch + fmt.Println(val) +} + +func handler() { + ch := make(chan int) + go danglingReceiver(ch) +} +``` + +Let's have look at the common scenarios in which this can happen. +**Sender forgets to close the channel** + +```go +func danglingReceiver(ch chan string) { + for val := range ch { + fmt.Println(val) + } + + fmt.Println("Done.. exiting") +} + +func handler(arr []string) { + ch := make(chan string, len(arr)) + + for _, data := range arr { + ch <- data + } + + go danglingReceiver(ch) +} +``` + +Here the Goroutine is expected to process the data sent on the channel by the handler function and terminate. But in reality, the channel even though its empty it's not closed. So, the worker keeps waiting for sender to send more data and never terminates. + +**Wrong Placement of Sender** +```go +func danglingReceiver(ch chan []string) { + val := <-ch + fmt.Println(val) +} + +func handler() error { + ch := make(chan []string) + go danglingReceiver(ch) + + data, err := someOtherValidationLogic() + if err != nil { + return errors.New("Validation Error, Exiting..!") + } + + ch <- data + + return nil +} +``` +Here, the handler spawns the Goroutine and quickly moves on to some validation logic. But, in the case of that validation returning an error the handler exits, and there is no one present to send data to the channel. Hence, the receiver is abandoned waiting for the data. + + +**Solution to The Abandoned Receiver** +To summarize, the receivers are dangling as they expect incoming data from the channel. Thus, the receiver gets blocked and wait forever. + +The solution is to defer closing of the channel. +```go +defer close(ch) +``` +It's a recommendation practice to defer the channel's closing as you spawn a new channel.Which will ensure the channel is closed when the function exits. +This can help the receiver to tell if channel is closed and it will terminate accordingly. + +```go +func danglingReceiver(ch chan int) { + //This won't be blocked + val := <-ch + fmt.Println(val) +} + +func handler() { + ch := make(chan int) + + + //Defer the closing of channel + defer close(ch) + go danglingReceiver(ch) +} +``` + + + From 8171ed4e94675301b50931c0885d5c33d63a1753 Mon Sep 17 00:00:00 2001 From: pypalkar23 <7241388+pypalkar23@users.noreply.github.com> Date: Wed, 7 Dec 2022 15:10:38 -0500 Subject: [PATCH 4/7] Updating Summary --- src/SUMMARY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 9b39c09..8c6f8d9 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -24,6 +24,7 @@ Summary * [Communication Security](communication-security/README.md) * [HTTP/TLS](communication-security/http-tls.md) * [WebSockets](communication-security/websockets.md) + * [gRPC](communication-security/grpc.md) * [System Configuration](system-configuration/README.md) * [Database Security](database-security/README.md) * [Connections](database-security/connections.md) @@ -32,6 +33,7 @@ Summary * [Stored Procedures](database-security/stored-procedures.md) * [File Management](file-management/README.md) * [Memory Management](memory-management/README.md) +* [Process Management](process-management/README.md) * General Coding Practices * [Cross-Site Request Forgery](general-coding-practices/cross-site-request-forgery.md) * [Regular Expressions](general-coding-practices/regular-expressions.md) From 4a5742a7801e3e9ef3107a050ecfe6b107721071 Mon Sep 17 00:00:00 2001 From: Mandar Palkar Date: Wed, 7 Dec 2022 16:57:39 -0500 Subject: [PATCH 5/7] Update grpc.md --- src/communication-security/grpc.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/communication-security/grpc.md b/src/communication-security/grpc.md index 3a58565..6588e7d 100644 --- a/src/communication-security/grpc.md +++ b/src/communication-security/grpc.md @@ -92,7 +92,7 @@ func main() { ``` -**Important** : As mentioned in the previous sections that the TLS certificates should be valid, should not be expired and should be installed with intermediate certificates when required as recommended in OWASP SCP Quick +**Important** : As mentioned in the previous sections that the TLS certificates should be valid, should not be expired and should be installed with intermediate certificates when required as recommended in [OWASP SCP Quick Reference Guide][4]. Also, an ideal gRPC client should avoid connecting to a gRPC server which is missing or has invalid certificates. The `InsecureSkipVerify` flag should never be set to false for services deployed in production. From 34d5df4465238ac95c209a3fba66621b4f93f779 Mon Sep 17 00:00:00 2001 From: Mandar Palkar Date: Wed, 7 Dec 2022 16:59:10 -0500 Subject: [PATCH 6/7] Fixing markdown in Memory management section --- src/memory-management/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/memory-management/README.md b/src/memory-management/README.md index 6785a7e..719fd82 100644 --- a/src/memory-management/README.md +++ b/src/memory-management/README.md @@ -120,7 +120,7 @@ func customizedSubstring(paramStr string) { } ``` -# Concatenation +## Concatenation ```go func customizedSubstring(paramStr string) { strSlice = (" " + paramStr[:100][:1]) @@ -131,7 +131,7 @@ func customizedSubstring(paramStr string) { } ``` -# Using a string Builder +## Using a string Builder ```go func customizedSubstring(paramStr string) { var temp strings.Builder From 598bb1b8278f881e885e6adcd0bb5e6819a8f05f Mon Sep 17 00:00:00 2001 From: pypalkar23 <7241388+pypalkar23@users.noreply.github.com> Date: Sun, 11 Dec 2022 10:53:48 -0500 Subject: [PATCH 7/7] Correcting Typos --- src/memory-management/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/memory-management/README.md b/src/memory-management/README.md index 719fd82..94390d5 100644 --- a/src/memory-management/README.md +++ b/src/memory-management/README.md @@ -88,7 +88,7 @@ Quoting [Golang's Github](https://github.com/golang/go/issues/13761): ## Memory Leaking Scenarios Even though go is a memory-safe language. The go compiler has been written in a way that can cause it to kind-of memory leaking issues sometimes. Since memory leaking is one of the ways using which an attacker can launch attacks like DDoS, one needs to be aware of recommended practices to prevent writing programs that can lead to memory leaking.Let's look at some of the scenarios. -## Memory leak. because of substrings: +### Memory leak because of substrings: For example, in the below function the memory occupied by the paramStr variable won't be garbage collected even though the function has returned as the strSlice variable is sharing the same underlying memory block. ```go @@ -106,7 +106,7 @@ func customizedSubstring(paramStr string) { ``` To get around such scenario, we can opt for multiple ways to efficiently copy a string, few of those are listed below: -## Using a byte array +#### Using a byte array ```go func customizedSubstring(paramStr string) { @@ -120,7 +120,7 @@ func customizedSubstring(paramStr string) { } ``` -## Concatenation +#### Concatenation ```go func customizedSubstring(paramStr string) { strSlice = (" " + paramStr[:100][:1]) @@ -131,7 +131,7 @@ func customizedSubstring(paramStr string) { } ``` -## Using a string Builder +#### Using a string Builder ```go func customizedSubstring(paramStr string) { var temp strings.Builder @@ -146,7 +146,7 @@ func customizedSubstring(paramStr string) { } ``` -## Memory Leaking with Pointers: +### Memory Leaking with Pointers: Let's look at the function below. Once the function's lifespan is over, the memory reference allocated for the the first and the last elements of the slice will be lost. @@ -173,7 +173,7 @@ func slicePointerFunc() []*string { } ``` -## Memory leaking because of deferred function calls: +### Memory leaking because of deferred function calls: Sometimes a deferred call queue can consume significant memory and may hold on to resources which are needed by other programs/subprograms running on the system. For example, look at the below program, which writes to number of files during it's lifespan. Such program might hold on to the file handlers till the very end of the function, even after finishing updating those files much earlier. ```go